一、进程和多线程的概念以及线程的优点

  打开Windo任务管理器可以看到很多正在运行着的exe程序,完全可以将运行在内存中的exe文件理解成进程,进程是受操作系统管理的基本运行单元。

  线程可以理解成在进程中独立运行的子任务。比如,QQ.exe运行时就有很多的子任务在同时运行。

  使用线程,可以最大限度地利用CPU的空闲时间来处理其他的任务,CPU在人物之间不停地切换,由于切换速度非常快,所以人们感觉这些任务似乎在同时运行。也就是说看,可以在同一时间内运行更多不同种类的任务,可以大幅增加CPU的利用率。

  二、使用多线程

  一个进程正在运行时至少会有一个线程在运行。

 package test;

 public class Test {

     public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
} }

  输出为:main。这里输出的main其实就是一个名称叫做main的线程在执行main()方法中的代码。

  1.继承Thread类

  实现多线程编程的方式主要有两种:一种是继承Thread类,另一种是实现Runnable接口。从Thread类的结构来看,Thread类实现了Runnable接口,它们之间具有多台关系。为了支持多继承,完全可以实现Runnable接口的形式,一边实现一边继承。

  1.1线程的调用的随机性

 public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
 package test;

 import com.mythread.www.MyThread;

 public class Run {

     public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();
System.out.println("运行结束");
} }
运行结束
MyThread

  从输出结果可以看出,代码的运行结果与代码执行顺序或调用顺序是无关的,线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。

  1.2 演示线程的随机性

 package mythread;

 public class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
int time = (int) (Math.random() * 1000);
Thread.sleep(time);
System.out.println("run=" + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
 package test;

 import mythread.MyThread;

 public class Test {
public static void main(String[] args) {
try { MyThread thread = new MyThread();
thread.setName("myThread");
thread.start(); for (int i = 0; i < 10; i++) {
int time = (int) (Math.random() * 1000);
Thread.sleep(time);
System.out.println("main=" + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
main=main
main=main
run=myThread
main=main
run=myThread
run=myThread
main=main
main=main
run=myThread
main=main
main=main
run=myThread
main=main
main=main
run=myThread
run=myThread
run=myThread
main=main
run=myThread
run=myThread

  从输出的20个结果可以看出,Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程,具有异步执行的效果。如果调用thread.run()方法就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。

  1.3 start()方法的顺序并不代表线程启动的顺序

 package extthread;

 public class MyThread extends Thread {

     private int i;

     public MyThread(int i) {
super();
this.i = i;
} @Override
public void run() {
System.out.println(i);
} }
 package test;

 import extthread.MyThread;

 public class Test {

     public static void main(String[] args) {
MyThread t11 = new MyThread(1);
MyThread t12 = new MyThread(2);
MyThread t13 = new MyThread(3);
MyThread t14 = new MyThread(4);
MyThread t15 = new MyThread(5);
MyThread t16 = new MyThread(6);
MyThread t17 = new MyThread(7);
MyThread t18 = new MyThread(8);
MyThread t19 = new MyThread(9);
MyThread t110 = new MyThread(10);
MyThread t111 = new MyThread(11);
MyThread t112 = new MyThread(12);
MyThread t113 = new MyThread(13); t11.start();
t12.start();
t13.start();
t14.start();
t15.start();
t16.start();
t17.start();
t18.start();
t19.start();
t110.start();
t111.start();
t112.start();
t113.start(); } }
2
3
1
4
5
7
6
8
10
9
11
12
13

  从输出结果可以看出,start()方法的顺序并不代表线程启动的顺序。

  2.实现Runnable接口

  如果想要创建的线程类已经有一个父类了,这时就不能再继承自Thread类了,因为Java不支持多继承,所以就需要实现Runnable接口。

 package myrunnable;

 public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("myrunnable类中的run!");
}
}
 package test;

 import myrunnable.MyRunnable;

 public class Run {

     public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("run中的main!");
} }
run中的main!
myrunnable类中的run!

  Thread.java类的8个构造函数中,有两个可以传递Runnable接口的对象,并且由于Thread类也实现了Runnable接口,所以也就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象, 还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他的线程进行调用。

  3.实例变量与线程安全

  自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。

  3.1 不共享数据

 package mythread;

 public class MyThread extends Thread {

     private int count = 5;

     public MyThread(String name) {
super();
this.setName(name);
} @Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("由" + this.currentThread().getName() + " 计算, count=" + count);
}
}
}
 package test;

 import mythread.MyThread;

 public class Run {
public static void main(String[] args) {
MyThread a = new MyThread("A");
MyThread b = new MyThread("B");
MyThread c = new MyThread("C");
a.start();
b.start();
c.start();
}
}
由B 计算, count=4
由C 计算, count=4
由A 计算, count=4
由A 计算, count=3
由C 计算, count=3
由B 计算, count=3
由C 计算, count=2
由A 计算, count=2
由C 计算, count=1
由C 计算, count=0
由B 计算, count=2
由B 计算, count=1
由B 计算, count=0
由A 计算, count=1
由A 计算, count=0

  一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享。

  3.2 共享数据

  共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。

  3.2.1“非线程安全”的问题

 package mythread;

 public class MyThread extends Thread {

     private int count=5;

     @Override
public void run() {
super.run();
count--;
System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
}
}
 package test;

 import mythread.MyThread;

 public class Run {
public static void main(String[] args) {
MyThread mythread=new MyThread(); Thread a=new Thread(mythread,"A");
Thread b=new Thread(mythread,"B");
Thread c=new Thread(mythread,"C");
Thread d=new Thread(mythread,"D");
Thread e=new Thread(mythread,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
由B计算,count=3
由A计算,count=3
由C计算,count=2
由D计算,count=1
由E计算,count=0

  从输出结果可以看出,线程B和Acount的值都是3,说明A和B同时对count进行处理,产生了“非线程安全”问题。

  3.2.2解决“非线程安全”的问题

  将MyThread类修改一下即可。

 package mythread;

 public class MyThread extends Thread {

     private int count=5;

     @Override
synchronized public void run() {
super.run();
count--;
System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
}
}
由A计算,count=4
由B计算,count=3
由E计算,count=2
由D计算,count=1
由C计算,count=0

  通过在run方法前加入synchronized关键字,使多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束后才可以执行run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果。

  synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程就可以执行synchronized里面的代码。如果不能拿到这把锁,那么这个线程就会不断地尝试拿这把锁,直到能拿到为止,而且是多个线程同时去争抢这把锁。

  3.2.3另一个解决“非线程安全”的示例

 package controller;

 //生成一个servlet组件
public class LoginServlet { private static String usernameRef;
private static String passwordRef; public static void doPost(String username, String password) {
try {
usernameRef = username;
if (username.equals("a")) {
Thread.sleep(5000);
}
passwordRef = password; System.out.println("username=" + usernameRef + " password="+ passwordRef);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
 package extthread;

 import controller.LoginServlet;

 public class ALogin extends Thread {
@Override
public void run() {
LoginServlet.doPost("a", "aa");
}
}
 package extthread;

 import controller.LoginServlet;

 public class BLogin extends Thread {
@Override
public void run() {
LoginServlet.doPost("b", "bb");
}
}
 package test;

 import extthread.ALogin;
import extthread.BLogin; public class Run { public static void main(String[] args) {
ALogin a = new ALogin();
a.start();
BLogin b = new BLogin();
b.start();
} }
username=b password=bb
username=b password=aa

  由于没有使用synchronized关键字,从而产生了“非线程安全”的问题。即在另一个线程没有等待sleep(5000)这个过程就执行了。修改代码使用synchronized关键字就可以解决这个问题。

 package controller;

 //生成一个servlet组件
public class LoginServlet { private static String usernameRef;
private static String passwordRef; synchronized public static void doPost(String username, String password) {
try {
usernameRef = username;
if (username.equals("a")) {
Thread.sleep(5000);
}
passwordRef = password; System.out.println("username=" + usernameRef + " password="+ passwordRef);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
username=a password=aa
username=b password=bb

  4.留意i--与System.out.println()的异常

  println()方法与i++联合使用时“有可能”出现的另外一种异常情况。

 package extthread;

 public class MyThread extends Thread {

     private int i = 5;

     @Override
public void run() {
System.out.println("i=" + (i--) + " threadName="+ Thread.currentThread().getName());
} }
 package test;

 import extthread.MyThread;

 public class Run {

     public static void main(String[] args) {

         MyThread run = new MyThread();

         Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
Thread t5 = new Thread(run); t1.start();
t2.start();
t3.start();
t4.start();
t5.start(); } }
i=5 threadName=Thread-4
i=5 threadName=Thread-1
i=4 threadName=Thread-3
i=3 threadName=Thread-5
i=2 threadName=Thread-2

  需要说明的是,虽然println()方法在内部是同步的(有synchronized关键字),但是i--的操作却是在进入println()之间发生的,所以有发生“非线程安全”问题的概率。所以还是应该使用synchronized关键字。

 package extthread;

 public class MyThread extends Thread {

     private int i = 5;

     @Override
synchronized public void run() {
System.out.println("i=" + (i--) + " threadName="+ Thread.currentThread().getName());
} }
i=5 threadName=Thread-1
i=4 threadName=Thread-2
i=3 threadName=Thread-3
i=2 threadName=Thread-5
i=1 threadName=Thread-4

  三、currentThread()方法

  currentThread()方法可返回代码段正在被哪个线程调用的信息。

  示例1:结果说明main方法被名为main的线程调用。

 package run;

 public class Run1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
main

  示例2:MyThread.java类的构造函数是被main线程调用的,而run方法时被名称为Thread-0的线程调用的,run方法是自动调用的方法。

 package mythread;

 public class MyThread extends Thread {

     public MyThread() {
System.out.println("构造方法的打印:" + Thread.currentThread().getName());
} @Override
public void run() {
System.out.println("run方法的打印:" + Thread.currentThread().getName());
} }
 package run;

 import mythread.MyThread;

 public class Run2 {
public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();
//mythread.run();
}
}
构造方法的打印:main
run方法的打印:Thread-0

  示例3:修改示例2中的Run2.java。从结果中我们可以看出MyThread.java类的构造函数和run方法都是被main线程调用的,这里也就是说明了同步和异步的区别。

package run;

import mythread.MyThread;

public class Run2 {
public static void main(String[] args) {
MyThread mythread = new MyThread();
//mythread.start();
mythread.run();
}
}
构造方法的打印:main
run方法的打印:main

  示例4:输出显示了this.getname()方法和currentThread().getname()的区别。

 package mythread;

 public class CountOperate extends Thread {

     public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
System.out.println("this.getName()=" + this.getName());
System.out.println(Thread.currentThread() == this);
System.out.println("CountOperate---end");
} @Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
System.out.println("this.getName()=" + this.getName());
System.out.println(Thread.currentThread() == this);
System.out.println("run---end");
} }
package test;

import mythread.CountOperate;

public class Run {

    public static void main(String[] args) {
CountOperate c = new CountOperate();
Thread t1 = new Thread(c);
t1.setName("test");
t1.start();
} }
CountOperate---begin
Thread.currentThread().getName()=main
this.getName()=Thread-0
false
CountOperate---end
run---begin
Thread.currentThread().getName()=test
this.getName()=Thread-0
false
run---end

  首先分析构造函数,根据输出可以看出,调用构造函数的是main线程,而此时还没有启动CountOperate子线程,所以打印出this.getName()=Thread-0,并且此时this代表的是CountOperate对象的实例,因此会输出false。

  然后分析run方法,从输出上来看,很让人疑惑的是run中的this.getName()=Thread-0这条输出。

  通过查看Thread类的源码,可以发现

  public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}

  在Thread类的构造方法中,默认地会通过this.name=name给name赋值。也就是说,在Thread源码中实际上new Thread(c)会将c的应用对象绑定到一个private变量target上,在t1被执行的时候,即t1.run被调用的时候,它会调用target.run方法,也就是说它是直接调用c对象的run方法,也就是说,在run方法被执行的时候,this.getName()实际上返回的是target.getName()方法,而Thread.currentThread().getName()实际上是t1.getName()。

  示例5:修改示例4中的main方法

package test;

import mythread.CountOperate;

public class Run {

    public static void main(String[] args) {
CountOperate c = new CountOperate();
c.setName("test");
c.start();
} }
CountOperate---begin
Thread.currentThread().getName()=main
this.getName()=Thread-0
false
CountOperate---end
run---begin
Thread.currentThread().getName()=test
this.getName()=test
true
run---end

  从输出结果中也可以看出,此时CountOperate构造函数和之前的输出保持一致,因为执行构造方法时线程还没有执行,所以this.getName()=Thread-0。而run方法中CountOperate线程已经执行并且可以获取到线程的名称,所以输出this.getName()=test。这也就是自动调用和被动调用的区别。

  四、isAlive()方法

  isAlive()方法用于判断当前的线程是否处于活动状态。

  示例1:end ==true说明mythread线程还没有执行完毕

package mythread;

public class MyThread extends Thread {
@Override
public void run() {
System.out.println("run=" + this.isAlive());
}
}
package run;

import mythread.MyThread;

public class Run {
public static void main(String[] args) {
MyThread mythread = new MyThread();
System.out.println("begin ==" + mythread.isAlive());
mythread.start();
System.out.println("end ==" + mythread.isAlive());
}
}
begin ==false
end ==true
run=true

  示例2:修改示例1程序。end ==false说明mythread对象已经在1秒之内执行完毕。

package run;

import mythread.MyThread;

public class Run {
public static void main(String[] args) throws InterruptedException{
MyThread mythread = new MyThread();
System.out.println("begin ==" + mythread.isAlive());
mythread.start();
Thread.sleep(1000);
System.out.println("end ==" + mythread.isAlive());
}
}
begin ==false
run=true
end ==false

  示例3:将线程对象以构造参数的方式传递给Thread对象进行start()启动时,运行的结果会有差异。造成这种结果的原因和之前遇到的情况一样,也就是Thread.currentThread()和this的区别。

package mythread;

public class CountOperate extends Thread {

    public CountOperate() {
System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()="+ Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end");
} @Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()="+ Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run---end");
} }
package test;

import mythread.CountOperate;

public class Run {

    public static void main(String[] args) {
CountOperate c = new CountOperate();
Thread t1 = new Thread(c);
System.out.println("main begin t1 isAlive=" + t1.isAlive());
t1.setName("test");
t1.start();
System.out.println("main end t1 isAlive=" + t1.isAlive());
} }
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
main begin t1 isAlive=false
main end t1 isAlive=true
run---begin
Thread.currentThread().getName()=test
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end

  五、sleep()方法

  sleep()方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是只this.currentThread()返回的线程。

  示例1:直接使用mythread.run()方法同步启动线程,这就使得通过main线程来执行MyThread1的线程,通过输出可以发现线程休眠了2秒。

package mythread;

public class MyThread1 extends Thread {
@Override
public void run() {
try {
System.out.println("run threadName="+ this.currentThread().getName() + " begin");
Thread.sleep(2000);
System.out.println("run threadName="+ this.currentThread().getName() + " end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package run;

import mythread.MyThread1;

public class Run1 {
public static void main(String[] args) {
MyThread1 mythread = new MyThread1();
System.out.println("begin =" + System.currentTimeMillis());
mythread.run();
System.out.println("end =" + System.currentTimeMillis());
}
}
begin =1525254700337
run threadName=main begin
run threadName=main end
end =1525254702337

  示例2:在main方法中使用mythread.start()方法后,main线程和MyThread2线程异步执行,所以首先打印信息为begin和end。而MyThread2线程是随后运行的,在最后两行打印run begin和run end。

package mythread;

public class MyThread2 extends Thread {
@Override
public void run() {
try {
System.out.println("run threadName="+ this.currentThread().getName() + " begin ="+ System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("run threadName="+ this.currentThread().getName() + " end ="+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package run;

import mythread.MyThread2;

public class Run2 {
public static void main(String[] args) {
MyThread2 mythread = new MyThread2();
System.out.println("begin =" + System.currentTimeMillis());
mythread.start();
System.out.println("end =" + System.currentTimeMillis());
}
}
begin =1525254898564
end =1525254898564
run threadName=Thread-0 begin =1525254898564
run threadName=Thread-0 end =1525254900564

  六、getId()方法

  getId()方法的所用是取得线程的唯一标识。从打印结果看,当前执行代码的线程名称为main,线程id为1。

public class Run1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getId());
}
}
main
1

  七、停止线程

  在Java中有一下3中方法可以终止正在运行得线程:

  ①使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

  ②使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend即resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。

  ③使用interrupt方法中断线程。

  1.停止不了的线程

  示例:通过调用interrupt()方法来停止线程,但interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt()方法仅仅是在当前线程打了一个停止的标记,并不是真的停止线程。从运行的结果也可以看出,调用interrupt方法并没有停止线程。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run {

    public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
} }
...
i=499991
i=499992
i=499993
i=499994
i=499995
i=499996
i=499997
i=499998
i=499999
i=500000

  2.判断线程是否是停止状态

  判断线程的状态是不是停止的,Thread.java类里提供了两种方法:①this.interrupted()测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程②this.isInterrupted()测试线程是否已经中断

  示例1:this.interrupted()使用。从打印情况来看,线程并未停止,这也就证明了interrupted()方法是测试当前线程是否已经中断,这个“当前线程”是main,它从未中断过,所以打印的结果是两个false。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
//Thread.currentThread().interrupt();
System.out.println("是否停止1?="+thread.interrupted());
System.out.println("是否停止2?="+thread.interrupted());
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
i=125615
i=125616
i=125617
是否停止1?=false
i=125618
i=125619
i=125620
i=125621
i=125622
是否停止2?=false
end!
i=125623
i=125624
i=125625

  示例2:修改示例1中的run方法。Thread.currentThread().interrupt();确实使main线程产生了中断效果。但是令人困惑的是,是否停止2?=false这是因为,如果连续两次调用interrupt()方法,则第一次调用已经清楚了其中断状态后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外,其他情况下都会返回false。

package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run2 {
public static void main(String[] args) {
Thread.currentThread().interrupt();
System.out.println("是否停止1?=" + Thread.interrupted());
System.out.println("是否停止2?=" + Thread.interrupted());
System.out.println("end!");
}
}
是否停止1?=true
是否停止2?=false
end!

  示例3:isInterrupted()方法并未清除状态标志,所以打印了两个true,和前面interrupted()方法的区别就是是否清除标志。

package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run3 {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("是否停止1?="+thread.isInterrupted());
System.out.println("是否停止2?="+thread.isInterrupted());
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
i=143424
i=143425
是否停止1?=true
i=143426
i=143427
i=143428
i=143429
i=143430
i=143431
i=143432
i=143433
i=143434
i=143435
i=143436
i=143437
i=143438
i=143439
i=143440
i=143441
i=143442
i=143443
i=143444
i=143445
i=143446
i=143447
i=143448
i=143449
i=143450
i=143451
i=143452
是否停止2?=true
i=143453

  3.能停止的线程--异常法

  可以在for语句来判断一下线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可。

  示例1:虽然停止了线程,但是如果for语句下面还有语句,还是会继续运行的。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!");
break;
}
System.out.println("i=" + (i + 1));
}
}
}
package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run {

    public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
} }
i=252895
i=252896
i=252897
i=252898
end!
已经是停止状态了!

  示例2:验证如果for语句下面还有语句时,还是会继续运行的。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("如果此代码是for又继续运行,线程并未停止!");
}
}
package test;

import exthread.MyThread;
import exthread.MyThread; public class Run { public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
} }
i=251852
i=251853
i=251854
已经是停止状态了!
如果此代码是for又继续运行,线程并未停止!
end!

  示例3:解决语句继续运行的问题,通过异常语句来停止线程。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for语句下面,你猜我会不会输出");
} catch (InterruptedException e) {
System.out.println("进run方法中的catch了");
e.printStackTrace();
}
}
}
i=238006
i=238007
end!
已经是停止状态了!
进run方法中的catch了
java.lang.InterruptedException
at exthread.MyThread.run(MyThread.java:11)

  4.在沉睡中停止

  示例1:在sleep()状态下停止线程(先sleep然后再interrupt()),会进入catch语句,并且清除停止状态值,使之变成false。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入了catch!"+this.isInterrupted());
e.printStackTrace();
}
}
}
package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run {

    public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
} }
run begin
end!
在沉睡中被停止!进入了catch!false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at exthread.MyThread.run(MyThread.java:9)

  示例2:先执行interrupt停止线程,因此输出end!然后for循环执行完成,然后进入sleep,产生异常,进入catch。

package exthread;

public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for(int i=0;i<100000;i++){
System.out.println("i="+(i+1));
}
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先执行interrupt停止线程,再遇到sleep!进入catch!");
e.printStackTrace();
}
}
}
package test;

import exthread.MyThread;

import exthread.MyThread;

public class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("end!");
}
}
end!
i=1
i=2
i=3
i=4
i=5
i=6
i=7
...
i=99998
i=99999
i=100000
run begin
先执行interrupt停止线程,再遇到sleep!进入catch!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at exthread.MyThread.run(MyThread.java:12)

  5.能停止的线程--暴力停止

  示例:使用stop()方法停止线程则是非常暴力的。从输出可以看出,main线程停止了8秒然后被stop掉了,然后子线程每隔一秒执行输出一个数字。

package testpackage;

public class MyThread extends Thread {
private int i = 0; @Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
package test.run;

import testpackage.MyThread;

public class Run {

    public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(8000);
thread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8

  6.方法stop()与java.lang.ThreadDeath异常

  示例:调用stop()方法会抛出java.lang.ThreadDeath异常,但在通常情况下,次异常不需要显示地捕捉。不过需要注意的是,stop()方法已经被作废,因为如果强制让线程停止则有可能使一些请理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。

package testpackage;

public class MyThread extends Thread {
@Override
public void run() {
try {
this.stop();
} catch (ThreadDeath e) {
System.out.println("进入run方法中的catch了");
e.printStackTrace();
}
}
}
package test.run;

import testpackage.MyThread;

public class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
进入run方法中的catch了
java.lang.ThreadDeath
at java.lang.Thread.stop(Unknown Source)
at testpackage.MyThread.run(MyThread.java:7)

  7.释放锁的不良后果

  示例:使用stop()释放锁将会给数据造成不一致的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误。这里的意思就是还没来得及给password赋值就把线程stop掉了。由于stop()方法已经作废了,所以不建议使用。

package testpackage;

public class SynchronizedObject {

    private String username = "a";
private String password = "aa"; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} synchronized public void printString(String username, String password) {
try {
this.username = username;
Thread.sleep(100000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
package testpackage;

public class MyThread extends Thread {

    private SynchronizedObject object;

    public MyThread(SynchronizedObject object) {
super();
this.object = object;
} @Override
public void run() {
object.printString("b", "bb");
}
}
package test.run;

import testpackage.MyThread;
import testpackage.SynchronizedObject; public class Run {
public static void main(String[] args) {
try {
SynchronizedObject object = new SynchronizedObject();
MyThread thread = new MyThread(object);
thread.start();
Thread.sleep(500);
thread.stop();
System.out.println(object.getUsername() + " "+ object.getPassword());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
b aa

  8.使用return停止线程

  示例:将方法interrupt()与return结合使用也能实现停止线程的效果。线程运行了2秒才停止。不过还是建议用“抛异常”的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播。

package extthread;

public class MyThread extends Thread {

    @Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("ֹͣ停止了!");
return;
}
System.out.println("timer=" + System.currentTimeMillis());
}
} }
package test.run;

import extthread.MyThread;

public class Run {

    public static void main(String[] args) throws InterruptedException {
MyThread t=new MyThread();
t.start();
Thread.sleep(2000);
t.interrupt();
} }
timer=1525267712005
timer=1525267712006
timer=1525267712006
timer=1525267712006
timer=1525267712006
...
timer=1525267714005
timer=1525267714005
timer=1525267714005
timer=1525267714005
timer=1525267714005
ֹͣ停止了!

  八、暂停线程

  暂停线程意味着次线程还可以恢复运行。在Java中,使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。

  1.suspend与resume方法的使用

  示例:通过输出我们可以看出,四行输出的时间间隔都是5秒,并且暂停和恢复控制线程中的run方法中的getI()方法的暂停和执行。

package mythread;

public class MyThread extends Thread {

    private long i = 0;

    public long getI() {
return i;
} public void setI(long i) {
this.i = i;
} @Override
public void run() {
while (true) {
i++;
}
} }
package test.run;

import mythread.MyThread;

public class Run {

    public static void main(String[] args) {

        try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(5000);
// A
thread.suspend();
System.out.println("A= " + System.currentTimeMillis() + " i="+ thread.getI());
Thread.sleep(5000);
System.out.println("A= " + System.currentTimeMillis() + " i="+ thread.getI());
// B
thread.resume();
Thread.sleep(5000);
// C
thread.suspend();
System.out.println("B= " + System.currentTimeMillis() + " i="+ thread.getI());
Thread.sleep(5000);
System.out.println("B= " + System.currentTimeMillis() + " i="+ thread.getI());
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
A= 1525268875079 i=2770802031
A= 1525268880079 i=2770802031
B= 1525268885079 i=5533965062
B= 1525268890079 i=5533965062

  2.suspend与resume方法的缺点--独占

  示例1:在使用suspend与resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。因为线程a被锁定,所以线程的方法也被锁定,其他线程就无法访问这个方法。即独占并锁死了printString方法。

package testpackage;

public class SynchronizedObject {

    synchronized public void printString() {
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")) {
System.out.println("a线程永远suspend了");
Thread.currentThread().suspend();
}
System.out.println("end");
} }
package test.run;

import testpackage.SynchronizedObject;

public class Run {

    public static void main(String[] args) {
try {
final SynchronizedObject object = new SynchronizedObject(); Thread thread1 = new Thread() {
@Override
public void run() {
object.printString();
}
}; thread1.setName("a");
thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread() {
@Override
public void run() {
System.out.println("thread2启动了,但进入不了printString方法");
System.out.println("因为printString方法被a线程锁定并且永远被suspend了");
object.printString();
System.out.println("你猜我会不会执行");
}
};
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
begin
a线程永远suspend了
thread2启动了,但进入不了printString方法
因为printString方法被a线程锁定并且永远被suspend了

  示例2:注意这里的main end!可以正常打印出来。将和之后的示例3作对比。

package mythread;

public class MyThread extends Thread {
private long i = 0; @Override
public void run() {
while (true) {
i++;
//System.out.println(i);
}
}
}
package test.run;

import mythread.MyThread;

public class Run {

    public static void main(String[] args) {

        try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("main end!");
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
main end!

  示例3:修改示例2中的MyThread。控制台将不再打印main end!,这是因为当程序运行到println()方法内部停止时,同步锁未被释放。下面看一下println()方法的源码

    public void println(long x) {
synchronized (this) {
print(x);
newLine();
}
}

  这导致当前PringStream的println()方法一直呈“暂停”状态,并且“锁未释放”,而main()方法当中的代码 System.out.println("main end!"); 迟迟不能执行打印!也就是线程中使用了println()方法然后被暂停,由于println()方法需要同步,即两个println()方法不能分别执行,所以就造成了第一个println()方法再暂停的情况下第二个不能执行的情况。

package mythread;

public class MyThread extends Thread {
private long i = 0; @Override
public void run() {
while (true) {
i++;
System.out.println(i);
}
}
}
...121036
121037
121038
121039
121040
121041

  3.suspend与resume方法的缺点--不同步

  示例:和前面的stop()方法使得数据不同步的情况一样,使用suspend与resume方法也会造成数据不同步的情况。线程a被暂停了导致password字段的值不能被更新,所以线程2在执行的时候保留默认值。

package myobject;

public class MyObject {

    private String username = "1";
private String password = "11"; public void setValue(String u, String p) {
this.username = u;
if (Thread.currentThread().getName().equals("a")) {
System.out.println("ֹͣ停止a线程");
Thread.currentThread().suspend();
}
this.password = p;
} public void printUsernamePassword() {
System.out.println(username + " " + password);
}
}
package test;

import myobject.MyObject;

public class Run {

    public static void main(String[] args) throws InterruptedException {

        final MyObject myobject = new MyObject();

        Thread thread1 = new Thread() {
public void run() {
myobject.setValue("a", "aa");
};
};
thread1.setName("a");
thread1.start(); Thread.sleep(500); Thread thread2 = new Thread() {
public void run() {
myobject.printUsernamePassword();
};
};
thread2.start(); } }
ֹͣ停止a线程
a 11

  九、yield()方法

  yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

  示例1:不添加yield()方法的情况。即线程独占CPU时间片来执行线程。用时21毫秒!

package test;

import extthread.MyThread;

import extthread.MyThread;

public class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
} }
package extthread;

public class MyThread extends Thread {

    @Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 50000000; i++) {
//Thread.yield();
count = count + (i + 1);
}
long endTime = System.currentTimeMillis();
System.out.println("用时" + (endTime - beginTime) + "毫秒!");
} }
用时21毫秒!

  示例2:添加yield()方法的情况。将CPU时间片让给其他资源导致速度变慢很多。用时13017毫秒!

package extthread;

public class MyThread extends Thread {

    @Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 50000000; i++) {
//Thread.yield();
count = count + (i + 1);
}
long endTime = System.currentTimeMillis();
System.out.println("用时" + (endTime - beginTime) + "毫秒!");
} }
用时13017毫秒!

  十、线程的优先级

  在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级高的线程对象中的任务。设置线程优先级有助于帮“线程规划器”确定在下一次选择哪一个线程来优先执行。

  Java中的线程优先级分为1~10这10个等级,使用MIN_PRIORITY/NORM_PRIORITY/MAX_PRIORITY三个常量来预置优先级的值。设置线程的优先级使用setPriority()方法,源码如下

    public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}

  1.线程优先级的继承特性

  在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

  示例1:优先级被继承,线程1启动线程2,两个线程的优先级是一样的,同时和mian线程也是一样的优先级。

package extthread;

public class MyThread1 extends Thread {
@Override
public void run() {
System.out.println("MyThread1 run priority=" + this.getPriority());
MyThread2 thread2 = new MyThread2();
thread2.start();
}
}
package extthread;

public class MyThread2 extends Thread {
@Override
public void run() {
System.out.println("MyThread2 run priority=" + this.getPriority());
}
}
package test;

import extthread.MyThread1;

public class Run {
public static void main(String[] args) {
System.out.println("main thread begin priority="+ Thread.currentThread().getPriority());
//Thread.currentThread().setPriority(6);
System.out.println("main thread end priority="+ Thread.currentThread().getPriority());
MyThread1 thread1 = new MyThread1();
thread1.start();
}
}
main thread begin priority=5
main thread end priority=5
MyThread1 run priority=5
MyThread2 run priority=5

  示例2:修改main方法。修改后main线程的优先级比原始的5高,通过main线程启动1线程,然后通过1线程启动2线程,优先级都是一样的。

package test;

import extthread.MyThread1;

public class Run {
public static void main(String[] args) {
System.out.println("main thread begin priority="+ Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
System.out.println("main thread end priority="+ Thread.currentThread().getPriority());
MyThread1 thread1 = new MyThread1();
thread1.start();
}
}
main thread begin priority=5
main thread end priority=6
MyThread1 run priority=6
MyThread2 run priority=6

  2.优先级具有规则性

  示例:高优先级的线程总是大部分先执行完,但并不代表高优先级的线程全部先执行完。另外,并不是MyThread先被main线程调用就会先执行完,当优先级等级差距很大时,谁先执行完和代码的调用顺序无关。即线程的优先级与代码执行顺序无关。

package extthread;

import java.util.Random;

public class MyThread1 extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
long addResult = 0;
for (int j = 0; j < 10; j++) {
for (int i = 0; i < 50000; i++) {
Random random = new Random();
random.nextInt();
addResult = addResult + i;
}
}
long endTime = System.currentTimeMillis();
System.out.println("******线程1 use time=" + (endTime - beginTime));
}
}
package extthread;

import java.util.Random;

public class MyThread2 extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
long addResult = 0;
for (int j = 0; j < 10; j++) {
for (int i = 0; i < 50000; i++) {
Random random = new Random();
random.nextInt();
addResult = addResult + i;
}
}
long endTime = System.currentTimeMillis();
System.out.println("%%%%%%线程2 use time=" + (endTime - beginTime));
}
}
package test;

import extthread.MyThread1;
import extthread.MyThread2; public class Run {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
MyThread1 thread1 = new MyThread1();
thread1.setPriority(1);
thread1.start(); MyThread2 thread2 = new MyThread2();
thread2.setPriority(10);
thread2.start();
}
}
}
%%%%%%线程2 use time=239
%%%%%%线程2 use time=256
%%%%%%线程2 use time=311
%%%%%%线程2 use time=312
%%%%%%线程2 use time=319
******线程1 use time=173
******线程1 use time=211
******线程1 use time=212
******线程1 use time=180
******线程1 use time=477

  3.优先级具有随机性

  优先级较高的线程不一定每一次都先执行完。

  示例:修改2中示例中的main方法,修改优先级为5和6,从结果可以看出,不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级较高的线程不一定每一次都先执行完run()方法中的任务,也就是说,线程优先级与打印顺序无关,不要讲这两者的关系相关联,它们的关系具有不确定性和随机性。

package extthread;

import java.util.Random;

public class MyThread1 extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Random random = new Random();
random.nextInt();
}
long endTime = System.currentTimeMillis();
System.out.println("**********1 use time=" + (endTime - beginTime));
}
}
%%%%%%%%%%2 use time=2
**********1 use time=6
**********1 use time=5
%%%%%%%%%%2 use time=0
**********1 use time=0
**********1 use time=1
%%%%%%%%%%2 use time=1
%%%%%%%%%%2 use time=0
**********1 use time=0
%%%%%%%%%%2 use time=1

  4.看谁运行得快

  示例:从输出结果可以看出,优先级高的线程运行得快,b的优先级为8,a的优先级为2,b运行得快得多,这里要注意的是,int类型的数一直加超过范围后会变成负数!!!

package extthread;

public class ThreadA extends Thread {

    private int count = 0;

    public int getCount() {
return count;
} @Override
public void run() {
while (true) {
count++;
}
} }
package extthread;

public class ThreadB extends Thread {

    private int count = 0;

    public int getCount() {
return count;
} @Override
public void run() {
while (true) {
count++;
}
} }
package test;

import extthread.ThreadA;
import extthread.ThreadB; public class Run { public static void main(String[] args) { try {
ThreadA a = new ThreadA();
a.setPriority(Thread.NORM_PRIORITY - 3);
a.start(); ThreadB b = new ThreadB();
b.setPriority(Thread.NORM_PRIORITY + 3);
b.start(); Thread.sleep(20000);
a.stop();
b.stop(); System.out.println("a=" + a.getCount());
System.out.println("b=" + b.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
} }
a=1197537357
b=-1459844607

  十一、守护线程

  在Java线程中有两种线程,一种是用户线程,另一种是守护线程。守护线程就是当进程中不存在非守护线程了,则守护线程自动销毁。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它就是一个很称职的守护者。

  示例:main线程休眠5秒后,最后一个非守护线程结束,守护线程也一同结束。

package testpackage;

public class MyThread extends Thread {
private int i = 0; @Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + (i));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package test.run;

import testpackage.MyThread;

public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
System.out.println("我离开thread对象也不再打印了,也就是停止了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
i=1
i=2
i=3
i=4
i=5
我离开thread对象也不再打印了,也就是停止了!

Java多线程编程(一)Java多线程技能的更多相关文章

  1. Java多线程编程(1)--Java中的线程

    一.程序.进程和线程   程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...

  2. Java并发编程:Java的四种线程池的使用,以及自定义线程工厂

    目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...

  3. Java多线程编程核心技术---Java多线程技能

    基本概念 进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据结构在处理机上顺序执行时所发生的活动,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的独立单位.线程可以理解成是在进 ...

  4. (1)Java多线程编程核心——Java多线程技能

    1.为什么要使用多线程?多线程的优点? 提高CPU的利用率 2.什么是多线程? 3.Java实现多线程编程的两种方式? a.继承Thread类 public class MyThread01 exte ...

  5. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  6. day05 Java网络编程socket 与多线程

    java网络编程 java.net.Socket Socket(套接字)封装了TCP协议的通讯细节,是的我们使用它可以与服务端建立网络链接,并通过 它获取两个流(一个输入一个输出),然后使用这两个流的 ...

  7. 《Java多线程编程核心技术》——多线程与同步

    Java多线程 线程可以理解为是在进程中独立运行的子任务. Java多线程 使用方法 Java中实现多线程主要有以下两种方法: 继承Thread,而后实例化该对象调用start()即启动了新线程; 实 ...

  8. Java并发编程:Java实现多线程的几种方式

    在Java中,多线程主要的实现方式有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService ...

  9. Java并发编程:Java创建线程的三种方式

    目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...

  10. Java并发编程(1)-Java内存模型

    本文主要是学习Java内存模型的笔记以及加上自己的一些案例分享,如有错误之处请指出. 一 Java内存模型的基础 1.并发编程模型的两个问题 在并发编程中,需要了解并会处理这两个关键问题: 1.1.线 ...

随机推荐

  1. 从零开始用golang创建一条简单的区块链

    区块链(Blockchain),是比特币的一个重要概念,它本质上是一个去中心化的数据库,同时作为比特币的底层技术,是一串使用密码学方法相关联产生的数据块,每一个数据块中包含了一批次比特币网络交易的信息 ...

  2. HNU_小初高数学学习软件_功能说明

    结对编程项目主要功能: 1.用户注册功能.用户提供手机号码,点击注册将收到一个注册码,用户可使用该注册码完成注册: 2.用户完成注册后,界面提示设置密码,用户输入两次密码匹配后设置密码成功.密码6-1 ...

  3. Maven 梳理-自动创建Maven项目(非web)

    mvn archetype:create和mvn archetype:generate create is deprecated in maven 3.0.5 and beyond,在maven3.0 ...

  4. 读《深入理解Elasticsearch》点滴-基础概念

    Lucene的概念 document:以json的形式体现,搜索和搜索的主要载体 field:document的一个部分 term(词项):代表文本中的一个词 token(词条):term在field ...

  5. Java后台开发方向面试题集合

    内容会不断更新. 初衷是每次看面经肯定都会有一些一时反应不过来的问题,希望集中记录一下便于自己查看. 而答案部分谷歌就很好,当然有些问题可能需要多次谷歌. 对于一些记不住的答案,我也会持续写上一些. ...

  6. springboot之本地缓存(guava与caffeine)

    1. 场景描述 因项目要使用本地缓存,具体为啥不用redis等,就不讨论,记录下过程,希望能帮到需要的朋友. 2.解决方案 2.1 使用google的guava作为本地缓存 初步的想法是使用googl ...

  7. Newtonsoft.Json.Linq 常用方法总结

    目录 1.Entity to Json 1.1.准备工作 1.2.Entity to Json 1.3.Json to Entity 2.Linq To Json 2.1.创建对象 2.2.从 Jso ...

  8. dubbo配置负载均衡、集群环境

    再用dubbo作为项目架构的时候,给consumer消费者用nginx提供了负载均衡策略和集群的实现, 但是想了下,consumer再多,但是提供者还是一个,最后还不都是落到了这一个provider上 ...

  9. Huffman树及其编解码

    Huffman树--编解码 介绍:   Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于 ...

  10. .net core 3.0 WPF中使用FolderBrowserDialog

    前言 随着.net core 3.0 的发布,WPF 也可以在 core 平台上使用了.当前的 WPF 不支持跨平台,仅能够在 Windows 平台上使用.如果想体验 WPF 跨平台开发,可以访问开源 ...