一、最常见的创建线程的两种方式
1.继承Thread
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/26.
*/
public class MyThread1 extends Thread {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread1 t2 = new MyThread1();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
【详解】继承Thread类,重写父类的run()方法,然后执行一系列操作。程序首先从mai()方法进入,然后通过new MyThread1()创建了两个对象,调用setName()可以修改线程名,调用start()方法可以让线程被开启。run()方法会被回调,其中currentThread()是静态方法,sleep()方法也是Thread的静态方法,代码里的sleep()其实就是this.sleep(),而this就是该方法被调用的对象,很显然是Thread.sleep();
2、实现Runnable接口
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/26.
*/
public class MyThread2 implements Runnable {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2();
MyThread2 t2 = new MyThread2();
Thread thread1=new Thread(t1);
Thread thread2=new Thread(t2);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
【详解】第二种方式是通过实现Runnable接口,重写run()方法,而我们还是需要newThread(),调用start()方法开启线程。这里的sleep()前面需要加Thread,因为此时的run()是new Thread()这个对象调用的,this指向的是new Thread()这个对象
【运行结果】
二、线程的生命周期
创建了线程之后,调用start()方法,线程进入就绪状态,只有当线程获得了CPU,就会到达运行状态,此时run()方法就会被执行,可能还没执行完,失去了CPU,就会回到就绪状态,等待下次获取到CPU之后继续执行run()方法。这就是为什么上述例子两个子线程交互执行for循环。通过调用sleep()方法可以让线程进入阻塞状态,等待时间到后又回到就绪状态。
三、给线程加锁
简单介绍java的内存分配,jvm内存主要包括
栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。
堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。
方法区:包含如下内容:
常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于方法区中。
代码段:用来存放从硬盘上读取的源程序代码。
数据段:用来存放static定义的静态成员。
》》其中每个线程都会有一个属于自己的栈,而堆、方法区都是所有线程共享的,这就存在数据同步的问题。比如在售票的时候,多个窗口去共享同一个数据,这个时候就会出现重票或负票的情况。线程在共享数据方面通过加锁来实现线程同步,就比如很多人排队上厕所,然后一个一个的进去,进去一个锁一下门,等他出来后把锁交给另一个人。
实现线程同步的方法有两种:
1.同步代码块
2.同步方法
1.实现Runnable的同步代码块
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest implements Runnable{
/**
* 线程同步:
* 1.同步代码块synchronized(同步监视器:锁){
* //需要同步的代码
* }
* 2.同步方法
*
*/
//任何对象都能充当锁
Object obj=new Object();
public int ticket=100;
@Override
public void run() {
while (true){
synchronized (obj) {//synchronized (this) {
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
}else {
break;
}
}
}
}
public static void main(String[] args) {
ThreadTest t1=new ThreadTest();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
2.实现Runnable的同步代码块
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest2 implements Runnable{
/**
* 线程同步:
* 1.同步代码块synchronized(同步监视器:锁){
* //需要同步的代码
* }
* 2.同步方法
*
*/
public int ticket=100;
@Override
public void run() {
while (true){
Boolean b=reduce();
if(b){
break;
}
}
}
public synchronized Boolean reduce(){
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
}else {
return true;
}
return false;
}
public static void main(String[] args) {
ThreadTest2 t1=new ThreadTest2();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
3.继承Thread的同步代码块
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest3 extends Thread {
public static int ticket = 100;
public static void main(String[] args) {
ThreadTest3 t1 = new ThreadTest3();
ThreadTest3 t2 = new ThreadTest3();
ThreadTest3 t3 = new ThreadTest3();
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
synchronized (ThreadTest3.class) {//synchronized (obj) {
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
} else {
break;
}
}
}
}
}
4.继承Thread的同步代码块
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest4 extends Thread {
public static int ticket = 100;
public static void main(String[] args) {
ThreadTest4 t1 = new ThreadTest4();
ThreadTest4 t2 = new ThreadTest4();
ThreadTest4 t3 = new ThreadTest4();
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
Boolean b=ThreadTest4.reduce();
if(b){
break;
}
}
}
public static synchronized boolean reduce() {
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
}else {
return true;
}
return false;
}
}
四、让线程堵塞和唤醒线程
package com.xioayu.threadTest2;
/**
* Created by hasee on 2019/5/24.
*/
public class Number implements Runnable {
public int ticket=100;
@Override
public void run() {
while (true){
synchronized (this) {
notifyAll();
if(ticket>0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
ticket--;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
public static void main(String[] args) {
Number t1=new Number();
new Thread(t1).start();
new Thread(t1).start();
}
}
【结果】
wait():让线程进入堵塞状态,并释放锁,只要不唤醒,一直在堵塞状态。Object类中申明,只能在同步方法块内调用
sleep() :让线程进入堵塞状态,不释放锁,时间结束后自动进入就绪状态,就是其他线程抢占了CPU也得不到锁,只有等该线程再次得到CPU执行结束或者释放锁后其他线程开可能得到锁。Thread类中申明,任何场景都可调用
notify()和notifyAll():唤醒线程,Object类中申明
生成者消费者例子
package com.xioayu.threadTest2;
/**
* Created by hasee on 2019/5/25.
*/
public class ThreadTest5 {
public static void main(String[] args) {
Apple apple=new Apple();
Consumer c1=new Consumer(apple);
Producer p1=new Producer(apple);
Producer p2=new Producer(apple);
Thread t1=new Thread(c1);
Thread t2=new Thread(p1);
Thread t3=new Thread(p2);
t1.setName("生成者1");
t2.setName("消费者1");
t3.setName("消费者2");
t1.start();
t2.start();
t3.start();
}
}
class Consumer implements Runnable{
private Apple apple;
Consumer(Apple apple){
this.apple=apple;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
apple.reduce();
}
}
}
class Producer implements Runnable{
private Apple apple;
Producer(Apple apple){
this.apple=apple;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
apple.add();
}
}
}
class Apple{
private int number=0;
public synchronized void add(){
if(number<5){
number++;
System.out.println("已供应第"+number+"个苹果");
notifyAll();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void reduce(){
if(number>0){
System.out.println("已消费第"+number+"个苹果");
number--;
notifyAll();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
五、未来任务
这是JDK5.0之后新增的一种创建线程的方式,让原本返回值为void的线程有了返回值,便于线程之间的通信
package com.xiaoyu.threadTest3;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Created by hasee on 2019/5/25.
*/
public class FutureTaskTest {
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<List<User>> listFutureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(listFutureTask);
thread.setName("子线程");
thread.start();
try {
List<User> list=listFutureTask.get();
for(User user:list){
System.out.println(user.getUsername()+"---"+user.getPassword());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<List<User>>{
@Override
public List<User> call() throws Exception {
List<User> list=new ArrayList<User>();
for (int i=0;i<10;i++) {
Thread.sleep(100);
User user = new User();
user.setUsername(i+"");
user.setPassword(i+"");
list.add(user);
}
return list;
}
}
class User{
private String username;
private String password;
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;
}
}
结果
六、线程池
package com.xioayu.threadTest2;
import java.util.concurrent.*;
/**
* Created by hasee on 2019/5/25.
*/
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
/* ThreadPoolExecutor service1= (ThreadPoolExecutor) service;
service1.setCorePoolSize(5);
service1.setKeepAliveTime();
service1.setMaximumPoolSize(15);*/
service.execute(new MyThread());
Future<Integer> submit = service.submit(new MyThread2());
try {
Integer integer = submit.get();
System.out.println("返回值"+integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i=i+2){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class MyThread2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i=1;i<100;i=i+2){
System.out.println(Thread.currentThread().getName()+":"+i);
}
return 3;
}
}
execute:实现Runnable接口
submit:实现Callable接口