Java笔记-多线程中同步加锁相关
Java程序入口就是由JVM启动的main线程:main线程又可以启动其他线程。当所有线程都运行结束时JVM退出,进程结束。守护线程(Daemon):守护线程是为其他线程服务的线程,所有的非守护线程都执行完毕后,虚拟机才会退出。守护线程的特点:不能持有资源(如打开文件等)创建守护线程:setDaemon(true);下面来演示下,子线程中有死循环,而主线程退出...
Java程序入口就是由JVM启动的main线程:
main线程又可以启动其他线程。当所有线程都运行结束时JVM退出,进程结束。
守护线程(Daemon):守护线程是为其他线程服务的线程,所有的非守护线程都执行完毕后,虚拟机才会退出。
守护线程的特点:不能持有资源(如打开文件等)
创建守护线程:
setDaemon(true);
下面来演示下,子线程中有死循环,而主线程退出了,子线程还没退出。
程序运行截图如下:
主线程已经退出,而子线程没有退出。
源码如下:
import java.text.SimpleDateFormat;
import java.util.Date;
class MyThread1 extends Thread{
@Override
public void run() {
while (true) {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
System.out.println(formatter.format(date));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main1 {
public static void main(String[] args) throws InterruptedException {
System.out.println("BEGIN");
MyThread1 myThread1 = new MyThread1();
myThread1.start();
Thread.sleep(1000 * 3);
System.out.println("OVER");
}
}
加上守护线程后,即可在main函数退出后,页退出!
程序运行截图如下:
源码如下:
import java.text.SimpleDateFormat;
import java.util.Date;
class MyThread1 extends Thread{
@Override
public void run() {
while (true) {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
System.out.println(formatter.format(date));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main1 {
public static void main(String[] args) throws InterruptedException {
System.out.println("BEGIN");
MyThread1 myThread1 = new MyThread1();
myThread1.setDaemon(true);
myThread1.start();
Thread.sleep(1000 * 3);
System.out.println("OVER");
}
}
下面是线程同步相关的设置:
多个线程同时运行,线程调度由操作系统决定,程序本身无法决定。
如下多个线程同时读取共享数据时:
会出现问题,程序运行截图如下:
代码如下:
class AddThread extends Thread{
@Override
public void run() {
for(int i = 0; i < Main2.LOOP; i++){
Main2.count += 1;
}
}
}
class DecThread extends Thread{
@Override
public void run() {
for(int i = 0; i < Main2.LOOP; i++){
Main2.count -= 1;
}
}
}
public class Main2 {
final static int LOOP = 10000;
public static int count = 0;
public static void main(String[] arg) throws InterruptedException {
AddThread addThread = new AddThread();
DecThread decThread = new DecThread();
addThread.start();
decThread.start();
addThread.join();
decThread.join();
System.out.println(count);
System.out.println("OVER");
}
}
对共享数据进行写入时,必须要保证是原子操作
Java使用synchronized对一个对象进行加锁!
synchronized的缺陷:性能会下降;
synchronized的使用:
找出修改共享变量线程代码块;选择一个实例作为锁;使用synchronized(lockObject){...}
运行截图如下:
代码如下:
class AddThread3 extends Thread{
@Override
public void run() {
for(int i = 0; i < Main2.LOOP; i++){
synchronized (Main3.LOCK) {
Main2.count += 1;
}
}
}
}
class DecThread3 extends Thread{
@Override
public void run() {
for(int i = 0; i < Main2.LOOP; i++){
synchronized (Main3.LOCK) {
Main2.count -= 1;
}
}
}
}
public class Main3 {
final static int LOOP = 10000;
public static int count = 0;
public static final Object LOCK = new Object();
public static void main(String[] arg) throws InterruptedException {
AddThread3 addThread = new AddThread3();
DecThread3 decThread = new DecThread3();
addThread.start();
decThread.start();
addThread.join();
decThread.join();
System.out.println(count);
System.out.println("OVER");
}
}
使用synchronized:不用担心异常!
public void add(int m){
synchronized (obj){
if(m < 0){
throw new RuntimeException();
}
this.value += m;
}//无论有无异常,都会在此释放
}
JVM规范定义了几种原子操作:
基本类型(long和double除外)赋值:int n = 100;
引用类型的赋值:List<String> list = anotherList;
下面是关于synchronized其他的写法
public synchronized void add(int n){
count += n;
total += n;
}
public void add(int n){
synchronized(this){
count += n;
total += n;
}
}
上面这两个写法是等价的。
如果一个类被设计为允许多线程正确访问:这个类就是“线程安全”的(thread-safe)
如java.lang.StringBuffer就是线程安全的。
synchronized是可以重复使用的。如下:
public void add(int m){
synchronized (lockA){
this.value += m;
synchronized (lockB){
this.another += m;
}//释放lockB
}//释放lockA
}
这里要注意:不同线程获取多个不同对象的锁可能会导致死锁。
死锁形成的条件(注意了,不管是哪个编程语言都差不多):
两个线程各自持有不同的锁;
两个线程各自试图获取对方已持有的锁;
双方无限等待下去:导致死锁。
死锁发送后:没有任何机制能解除死锁;只能强制结束JVM进程。
如何避免死锁:线程获取锁的顺序要一致!
如下实例!
源码如下:
class SharedObject{
final Object lockA = new Object();
final Object lockB = new Object();
int accountA = 1000;
int accountB = 2000;
public void a2b(int balance){
synchronized (lockA){
accountA -= balance;
synchronized (lockB){
accountB += balance;
}
}
}
public void b2a(int balance){
synchronized (lockB){
accountB -= balance;
synchronized (lockA){
accountA += balance;
}
}
}
}
class AThread extends Thread{
@Override
public void run() {
for(int i = 0; i < Main4.LOOP; i++){
Main4.shared.a2b(1);
System.out.println("ing...");
}
}
}
class BThread extends Thread{
@Override
public void run() {
Main4.shared.b2a(1);
for(int i = 0; i < Main4.LOOP; i++){
System.out.println("ing...");
}
}
}
public class Main4 {
final static int LOOP = 100000;
static SharedObject shared = new SharedObject();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new AThread();
Thread t2 = new AThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("OVER");
}
}
这个代码是有死锁的但不一定发送:
原因是这样的:
此处只要把锁的先后改成一样的就可以了!
更多推荐
所有评论(0)