JAVA线程与锁的笔记

关于创建线程常见的三个方式:

常见的Java线程的 4种创建方式分别为:继承Thread类、实现Runnable接口、通过ExecutorService和Callable<Class>实现有返回值的线程、基于线程池,如图 所示。

 

一.关于继承Thread类

继承Thread类
        Thread类实现了Runnable接口并定义了操作线程的一些方法,我们可以通过继承Thread类的方式创建一个线程。具体实现为创建一个类并继承Thread接口,然后实例化线程对象并调用start方法启动线程。start方法是一个native方法,通过在操作系统上启动一个新线程,并最终执行run方法来启动一个线程。run方法内的代码是线程类的具体实现逻辑。
简单的例子:

package com.test;

public class Thread_test{
	
	public static void main(String[] args) {
		Thread_new thread = new Thread_new();
		thread.start();
	}
}

class Thread_new extends Thread{
	
	public void run() {
		try {
			Thread.sleep(2000);
			System.out.println("我在这里,我在这里");
			Thread.sleep(2000);
			System.out.println("2秒后,我在这里,我在这里");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
}

结果如下:

我在这里,我在这里
2秒后,我在这里,我在这里

二.关于实现Runnable接口

       基于Java编程语言规范,如果子类已继承(extends)了一个类,就无法再继承Thread类,此时可通过实现Runnable接口创建线程。具体的实现过程为:通过实现Runnable接口创建ChildrenClassThread线程,实例化名称为childrenThread的线程实例,创建Thread类的实例并传入childrenThread线程实例,调用线程的start方法启动线程。

package com.test;

public class Runnable_Test {
	
	public static void main(String[] args) {
		Runnable_Impl runnable_Impl = new Runnable_Impl();
		Thread thread = new Thread(runnable_Impl);
		thread.start();
	}
}


class Runnable_Impl implements Runnable{ //这里实现Runnable的方法就能创建新的线程
	@Override
	public void run() {
		try {
			Thread.sleep(2000);
			System.out.println("我这里是第一次啊");
			Thread.sleep(2000);
			System.out.println("2秒后,我这里是第二次啊");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

结果如下:

我这里是第一次啊
2秒后,我这里是第二次啊

三.关于通过ExecutorService和Callable<Class>实现有返回值的线程

        在主线程中开启多个线程并发执行一个任务,然后收集各个线程执行返回的结果并将最终结果汇总起来,这时就要用到Callable接口。具体的实现方法为:创建一个类并实现Callable接口,在call方法中实现具体的运算逻辑并返回计算结果。具体的调用过程为:创建一个线程池、一个用于接收返回结果的Future List及Callable线程实例,使用线程池提交任务并将线程执行之后的结果保存在Future中,在线程执行结束后遍历Future List中的Future对象,在该对象上调用get方法就可以获取Callable线程任务返回的数据并汇总结果,实现代码如下:
 

package com.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyCallBale_Test {
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newFixedThreadPool(5);
		List<Future> list = new ArrayList<Future>();
		for (int i = 0; i < 5; i++) {
			Callable callable = new MyCallBale_Impl(i + " ");

			Future future = executorService.submit(callable);
			System.out.println("submit a callable thread:" + i);
			list.add(future);
		}
		executorService.shutdown();
		list.forEach(item -> {
			try {
				System.out.println(Thread.currentThread().getName() + " is running");
				System.out.println("get the result from callable thread:" + item.get().toString());
			} catch (InterruptedException | ExecutionException e) {
				e.printStackTrace();
			}
		});
		System.out.println("==================================================");
/*线程是非常宝贵的计算资源,在每次需要时创建并在运行结束后销毁是非常浪费资源的。
我们可以使用缓存策略并使用线程池来创建线程,具体过程为创建一个线程池并用该线程池提交线程任务.*/
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			pool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()+" is running");
				}
			});
		}
		pool.shutdown();
		
	}
}

class MyCallBale_Impl implements Callable<String> {

	private String name;

	public MyCallBale_Impl(String name) {
		this.name = name;
	}

	@Override
	public String call() throws Exception {
		return name;
	}
}

结果如下:

submit a callable thread:0
submit a callable thread:1
submit a callable thread:2
submit a callable thread:3
submit a callable thread:4
main is running
get the result from callable thread:0 
main is running
get the result from callable thread:1 
main is running
get the result from callable thread:2 
main is running
get the result from callable thread:3 
main is running
get the result from callable thread:4 
==================================================
pool-2-thread-1 is running
pool-2-thread-3 is running
pool-2-thread-4 is running
pool-2-thread-2 is running
pool-2-thread-5 is running
pool-2-thread-6 is running
pool-2-thread-7 is running
pool-2-thread-9 is running
pool-2-thread-10 is running
pool-2-thread-8 is running

四.关于synchronized关键字

1.synchronized作用于成员变量和非静态方法时,锁住的是对象的实例

   synchronized的作用范围:
◎ synchronized作用于成员变量和非静态方法时,锁住的是对象的实例,即this对象。
◎ synchronized作用于静态方法时,锁住的是Class实例,因为静态方法属于Class而不属于对象。
◎ synchronized作用于一个代码块时,锁住的是所有代码块中配置的对象。
示例代码:

package com.test;

public class Synchronied_Test {
	
	public static void main(String[] args) {
		final Synchronied_Impl synchronied_Impl = new Synchronied_Impl();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod1();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("=====================");
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod2();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}
}


class Synchronied_Impl {
	//synchronized修饰普通的同步方法 ,锁住的是当前的实例对象
	public synchronized void generalMethod1() throws InterruptedException {
		
		for (int i = 1; i < 3; i++) {
			System.out.println("GeneralMethod1 execute " +i+" time");
			Thread.sleep(3000);
		}
	}
	
	//synchronized修饰普通的同步方法 ,锁住的是当前的实例对象
	public synchronized void generalMethod2() throws InterruptedException {

		for (int i = 1; i < 3; i++) {
			System.out.println("GeneralMethod2 execute " +i+" time");
			Thread.sleep(3000);
		}
	}
}

        synchronized作用于成员变量和非静态方法时,锁住的是对象的实例,具体的代码实现如上面的程序定义了两个使用synchronized修饰的普通方法,然后在main函数中定义对象的实例并发执行各个方法。可以看到,线程 1会等待线程 2执行完成才能执行,这是因为synchronized锁住了当前的对象实例synchronizedDemo导致的。具体的执行结果如下:

=====================
GeneralMethod1 execute 1 time
GeneralMethod1 execute 2 time
GeneralMethod2 execute 1 time
GeneralMethod2 execute 2 time

2.synchronized作用静态同步方法

        当创建两个类实体,便可以同步执行,但他们锁定的依然是当前对象。

package com.test;

import org.apache.commons.lang.StringUtils;

public class Synchronied_Test2 {
	
	public static void main(String[] args) {
		final Synchronied_Impl2 synchronied_Impl = new Synchronied_Impl2();
		final Synchronied_Impl2 synchronied_Impl2 = new Synchronied_Impl2();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod1(true);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("=====================");
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl2.generalMethod1(false);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}
}


class Synchronied_Impl2 {
	//synchronized修饰普通的同步方法 ,锁住的是当前的实例对象
	public  synchronized void generalMethod1(boolean flag) throws InterruptedException {
		
		for (int i = 1; i < 3; i++) {
			System.out.println((flag?"--":"  ")+"GeneralMethod1 execute " +i+" time--");
			Thread.sleep(3000);
		}
	}
	
	//synchronized修饰普通的同步方法 ,锁住的是当前的实例对象
	public synchronized void generalMethod2() throws InterruptedException {

		for (int i = 1; i < 3; i++) {
			System.out.println("GeneralMethod2 execute " +i+" time");
			Thread.sleep(3000);
		}
	}
}
	 

        结果如下:

=====================
--GeneralMethod1 execute 1 time--
  GeneralMethod1 execute 1 time--
--GeneralMethod1 execute 2 time--
  GeneralMethod1 execute 2 time--

3.synchronized作用静态同步方法

        synchronized作用于静态同步方法,锁住的是当前类的Class对象。

package com.test;

import org.apache.commons.lang.StringUtils;

public class Synchronied_Test3 {
	
	public static void main(String[] args) {
		final Synchronied_Impl3 synchronied_Impl = new Synchronied_Impl3();
		final Synchronied_Impl3 synchronied_Impl2 = new Synchronied_Impl3();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod1(true);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("=====================");
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl2.generalMethod1(false);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}
}


class Synchronied_Impl3 {
	//synchronized修饰静态同步方法 ,锁住的是当前Class(就是这个类Synchronied_Impl3)对象
	public static synchronized void generalMethod1(boolean flag) throws InterruptedException {
		
		for (int i = 1; i < 5; i++) {
			System.out.println((flag?"--":"  ")+"GeneralMethod1 execute " +i+" time--");
			Thread.sleep(3000);
		}
	}
	
	//synchronized修饰静态同步方法 ,锁住的是当前Class对象
	public static synchronized void generalMethod2() throws InterruptedException {

		for (int i = 1; i < 5; i++) {
			System.out.println("GeneralMethod2 execute " +i+" time");
			Thread.sleep(3000);
		}
	}
}
	 

        结果如下:

=====================
--GeneralMethod1 execute 1 time--
--GeneralMethod1 execute 2 time--
--GeneralMethod1 execute 3 time--
--GeneralMethod1 execute 4 time--
  GeneralMethod1 execute 1 time--
  GeneralMethod1 execute 2 time--
  GeneralMethod1 execute 3 time--
  GeneralMethod1 execute 4 time--

        通过日志看到,static方法是属于Class,Class的相关数据在JVM中是全局共享,因此静态方法锁相当于类的一个全局锁,会锁住所有调用该方法的线程。

4.Synchronized作用于代码块中时。

synchronized作用于一个代码块时,锁住的是在代码块中配置的对象:

package com.test;

public class Synchronied_Test4 {
	
	public static void main(String[] args) {
		final Synchronied_Impl4 synchronied_Impl = new Synchronied_Impl4();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod1(true);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("=====================");
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod2(false);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}

}

class Synchronied_Impl4 {
	String lockA = "lockA";
	//synchronized修饰方法快时 ,锁住的是括号里面配置的对象
	public void generalMethod1(boolean flag) throws InterruptedException {
		
		synchronized(lockA) {
			for (int i = 1; i < 3; i++) {
				System.out.println((flag?"--":"  ")+"GeneralMethod1 execute " +i+" time--");
				Thread.sleep(3000);
			}
		};
		
	}
	
	//synchronized修饰方法快时 ,锁住的是括号里面配置的对象
	public void generalMethod2(boolean flag) throws InterruptedException {
		synchronized(lockA) {
			for (int i = 1; i < 3; i++) {
				System.out.println((flag?"--":"  ")+"GeneralMethod2 execute " +i+" time");
				Thread.sleep(3000);
			}
		}
	}
}
	 

以上代码的执行结果很简单,由于两个方法都需要获取名为lockA的锁,所以线程 1会等待线程2执行完成后才能获取该锁并执行,结果如下:
 

=====================
--GeneralMethod1 execute 1 time--
--GeneralMethod1 execute 2 time--
  GeneralMethod2 execute 1 time
  GeneralMethod2 execute 2 time

5.出现死锁的情况,作为参考对比

写多线程程序时可能会出现A线程依赖B线程中的资源,而B线程又依赖于A线程中的资源的情况,这时就可能出现死锁。我们在开发时要杜绝资源相互调用的情况。如下所示就是一段典型的死锁代码:

package com.test;

public class Synchronied_Test5 {
	
	public static void main(String[] args) {
		final Synchronied_Impl5 synchronied_Impl = new Synchronied_Impl5();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod1(true);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("=====================");
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronied_Impl.generalMethod2(false);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}

}

class Synchronied_Impl5 {
	String lockA = "lockA";
	String lockB = "lockB";
	//synchronized修饰方法快时 ,锁住的是括号里面配置的对象
	public void generalMethod1(boolean flag) throws InterruptedException {
		
		synchronized(lockA) {
			for (int i = 1; i < 3; i++) {
				System.out.println((flag?"--":"  ")+"GeneralMethod1 execute " +i+" time--");
				Thread.sleep(3000);
				synchronized (lockB) {//3秒后,线程2执行了generalMethod2,同时想锁定lockB	
				}
			}
		};
		
	}
	
	//synchronized修饰方法快时 ,锁住的是括号里面配置的对象
	public void generalMethod2(boolean flag) throws InterruptedException {
		synchronized(lockB) {
			for (int i = 1; i < 3; i++) {
				System.out.println((flag?"--":"  ")+"GeneralMethod2 execute " +i+" time");
				Thread.sleep(3000);
				synchronized (lockA) {//线程2执行generalMethod2,同时想锁定lockA	
				}
			}
		}
	}
}
	

通过以上代码可以看出,在blockMethod1方法中,synchronized(lockA)在第一次循环执行后想使用synchronized(lockB)锁住了lockB,但下次执行需等待lockA锁释放后才能继续;而在blockMethod2方法中,synchronized (lockB)在第一次循环执行后使用ynchronized(lockA)锁住了lockA,等待lockB释放后才能进行下一次执行。这样就出现blockMethod1等待blockMethod2释放lockA,而blockMethod2等待blockMethod1释放lockB的情况,就出现了死锁。执行结果是两个线程都挂起,等待对方释放资源,结果如下:
 

=====================
--GeneralMethod1 execute 1 time--
  GeneralMethod2 execute 1 time


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值