常见的Java线程的 4种创建方式分别为:继承Thread类、实现Runnable接口、通过ExecutorService和Callable<Class>实现有返回值的线程、基于线程池,如图 所示。
继承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秒后,我在这里,我在这里
基于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秒后,我这里是第二次啊
在主线程中开启多个线程并发执行一个任务,然后收集各个线程执行返回的结果并将最终结果汇总起来,这时就要用到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的作用范围:
◎ 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
当创建两个类实体,便可以同步执行,但他们锁定的依然是当前对象。
- 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--
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中是全局共享,因此静态方法锁相当于类的一个全局锁,会锁住所有调用该方法的线程。
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
写多线程程序时可能会出现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