从今天起,在本周末之前,我会整理出java多线程的知识,会搜集网络上的内容及图书馆中的书籍,解决多线程留下的疑惑。
本文会从以下几个方面介绍java多线程:简单介绍线程、线程创建、线程的生命周期、线程控制、线程安全、wait及notify、notifyAll。
以案例代码为主。
一:简单介绍线程
线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS。现在的多数操作系统都是多任务操作系
统,多线程是实现多任务的一种方式)在执行一个程序的时候,会在系统中建立一个进程,在这个进程中,必须至少建立一个线程(即
主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序至少有一个主线程。
二:线程创建
1.继承Thread类,重写run方法
2.实现Runnable接口,实现run方法
两种方法的区别:使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资
源共享。
public class Thread1 extends Thread{
public void run() {
while(true){
System.out.println("这是第一个线程!");
}
}
}
public class Thread2 implements Runnable{
public void run() {
while(true){
System.out.println("这是第二个线程!");
}
}
}
public class GO {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.start();
new Thread(t2).start();
}
}
三:线程的生命周期
线程存在以下5中状态
1.新建new出Thread类的对象
2.就绪调用start()方法,准备就绪,等待cpu进行调度
3.运行执行了run()方法
4.阻塞因为某些原因(如IO阻塞、睡眠、等待、因为所需要的对象被锁定……)暂停执行,可能将资源交给其他资源使用
5.死亡线程执行完毕,不在进行使用。(run()执行完毕)
四:线程的控制
1.join线程(夹三)让第二个线程执行,执行完毕后,再执行第一个线程。
public class TestJoin {
public static void main(String[] args) {
MainThread mt = new MainThread();
mt.start();
}
}
class MainThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
JoinThread join = new JoinThread();
join.start();
join.setName("Hello");
for (int i = 0; i < 50; i++) {
if (i == 10) {
try {
join.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.activeCount());
System.out.println(Thread.currentThread().getName() + "-----" + i);
}
}
}
class JoinThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "======" + i);
}
}
}
2.线程睡眠
Thread.sleep()该方法为静态方法。
该实例是每隔一秒打印当前时间,等5秒后程序终止。
<span style="font-size:14px;">import java.util.Date;
public class TestSleep {
public static void main(String[] args) {
TestThread test = new TestThread();
test.start();
try {
test.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
test.interrupt();
}
}
class TestThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("=====" + new Date() + "=====");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
}
}</span>
3.线程让步
yield()也是静态方法,获得执行机会的线程放弃本次机会,和另外的线程再争夺机会。
public class TestYield {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("A");
t1.start();
MyThread2 t2 = new MyThread2("B");
t2.start();
}
}
class MyThread2 extends Thread{
MyThread2(String s){
super(s);
}
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(getName()+" "+i);
if(i%10==0){
yield();
}
}
}
}
特别说明下sleep和yield的区别:
sleep是抱着cpu睡觉,不放弃执行权力,醒来立即执行
yield放弃执行权力,大家一起重新争夺。·
4.线程优先级问题
不起决定性作用,只是执行的几率不同而已
特别注意:在不同的操作系统上的JVM优先级的数字范围不同,windows上为1--10,而linux上为1--7.这就要求我们在用到的时候,尽量使
用java为我们定义好的优先级。
线程默认优先级是 5,Thread 类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY 线程可以具有的最高优先级。
static int MIN_PRIORITY 线程可以具有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级
五:线程安全
同步代码块、同步方法
Synchronized(……){}
这里有个实例,线程不安全测试:前提为一个对象,开启两个线程分别打印字符串“A”……"A""B"……"B"
先定义一个类,此类有方法printString(String s),生成一份该类的对象,开启两个线程调用printString(String s )方法
//不安全可能是有两个V对象,再或者就是一个V对象没加锁
public class TestUnsafe {
public static void main(String[] args) {
new Thread(new Runnable() {
V v = new V();
public void run() {
// TODO Auto-generated method stub
while(true){
v.PrintString("AAAAAAAAAA");
}
}
},"A").start();
new Thread(new Runnable() {
V v = new V();
public void run() {
// TODO Auto-generated method stub
while(true){
v.PrintString("BBBBBBBBBB");
}
}
},"B").start();
}
}
class V{
public synchronized void PrintString(String s){
for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i));
}
System.out.println();
}
}
注:这里存在两个问题导致线程不安全,两个V对象和没加同步监视器
会出现A和B交替出现,说明线程不安全。
我们可以利用同步代码块和同步方法解决此问题。
同步代码块
import com.langsin.base.TestUnsafe.V;
public class TestSynchronizedBlock {
public static void main(String[] args) {
TestSynchronizedBlock test = new TestSynchronizedBlock();
test.go();
}
private void go() {
// TODO Auto-generated method stub
final V v = new V();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
v.printString("AAAAAAAAAAAAAAAAAAAA");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
v.printString("BBBBBBBBBBBBBBBBBBBB");
}
}
}).start();
}
class V {
public void printString(String s) {
synchronized(this.getClass()){
for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i));
}
System.out.println();
}
}
}
}
同步方法
import com.langsin.base.TestUnsafe.V;
public class TestSynchronizedBlock {
public static void main(String[] args) {
TestSynchronizedBlock test = new TestSynchronizedBlock();
test.go();
}
private void go() {
// TODO Auto-generated method stub
final V v = new V();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
v.printString("AAAAAAAAAAAAAAAAAAAA");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
v.printString("BBBBBBBBBBBBBBBBBBBB");
}
}
}).start();
}
class V {
public void printString(String s) {
synchronized(this.getClass()){
for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i));
}
System.out.println();
}
}
}
}
六:wait、notify
实例:
1.两个线程A和B,线程A先执行,线程B再执行
public class TestWait {
public static void main(String[] args) {
TestWait test = new TestWait();
test.go();
}
boolean flag = true;
V v = new V();
private void go() {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public synchronized void run() {
// TODO Auto-generated method stub
while(true){
v.f1();
}
}
}, "A线程").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
v.f2();
}
}
},"B线程").start();
}
class V{
synchronized void f1(){
while (flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = true;
notifyAll();
}
synchronized void f2(){
while (!flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
flag = false;
notifyAll();
}
}
}
2.三个线程A和B、C,线程A先执行,然后线程B再执行,最后C再执行,然后再依次循环
package com.langsin.base;
public class TestThreeThreadInTurn {
public static void main(String[] args) {
TestThreeThreadInTurn test = new TestThreeThreadInTurn();
test.go();
}
V v = new V();
private void go() {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true)
v.f1();
}
}, "A线程").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true)
v.f2();
}
}, "B线程").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true)
v.f3();
}
}, "C线程").start();
}
int flag = 1;
class V {
synchronized void f1() {
while (flag != 1) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
flag = 2;
notifyAll();
System.out.println(Thread.currentThread().getName());
}
synchronized void f2() {
while (flag != 2) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
flag = 3;
notifyAll();
System.out.println(Thread.currentThread().getName());
}
synchronized void f3() {
while (flag != 3) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
flag = 1;
notifyAll();
System.out.println(Thread.currentThread().getName());
}
}
}
3.下面给大家介绍一个生产者和消费者问题
包括以下5个类,
ProductContainer容器,里面有一个linkedList。有最大容量,有往里放和往外拿的方法,单例模式
<p style="background:rgb(240,240,240);"><pre name="code" class="java">import java.util.LinkedList;
public class ProductContainer {
private ProductContainer(){}
private static ProductContainer instance = null;
public static synchronized ProductContainer getInstance(){ if(instance==null){ instance = new ProductContainer();
}
return instance;
}
private LinkedList<Product> list = new LinkedList<Product>();
private final int MAX_SIZE = 10;
public synchronized void putProduct(Product p){
while(list.size()>=MAX_SIZE){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.add(p);
System.out.println(Thread.currentThread().getName()+"生产了"+p);
notifyAll();
}
public synchronized Product getProduct(){
while(list.size()<=0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Product p = list.removeLast();
System.out.println(Thread.currentThread().getName()+"消费了"+p);
notifyAll();
return p ;
}
public void checkSize(){
System.out.println("现在的桌子上有:"+list.size()+"个");
}
}
Producer线程,生产者
public class Producer extends Thread {
private ProductContainer pc = null;
public Producer(ProductContainer pc){
this.pc = pc;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Product p = new Product(i,"蛋糕");
pc.putProduct(p);
}
}
}
Customer线程,消费者
<pre name="code" class="java">public class Customer extends Thread{
private ProductContainer pc = null;
public Customer(ProductContainer pc) {
this.pc = pc;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pc.getProduct();
}
}
}
Product产品
public class Producer extends Thread {
private ProductContainer pc = null;
public Producer(ProductContainer pc){
this.pc = pc;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Product p = new Product(i,"蛋糕");
pc.putProduct(p);
}
}
}
Go主程序main
public class Go {
public static void main(String[] args) {
final ProductContainer pc = ProductContainer.getInstance();
for (int i = 0; i < 2; i++) {
new Customer(pc).start();
}
for (int i = 0; i < 2; i++) {
new Producer(pc).start();
}
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pc.checkSize();
}
}
}).start();
}
}
必须注意的问题:
1.容器一定要定义容量
2.容器往里添加和删除都是阻塞的。wait、notify 注意wait方法要加synchronized修饰
3.消费者和生产者拿到的必须是一个容器(单例模式)
4.Future提前完成任务
假设有两个方法A和B,A调用B,如果B方法执行起来比较长,那么就会导致A方法的阻塞等待,直到B方法完毕后,A方法才能被返回。类似于购买物品,
先生成订单,然后等产品真正到位,我们再拿订单去换取产品。
有以下几个类和接口
Data接口
public interface Data {
public String obtainString();
}
Future
public class Future implements Data {
private boolean ready;
private Real real;
public synchronized void setReal(Real real){
if(ready){
return;
}
this.ready = true;
this.real = real;
notifyAll();
}
@Override
public synchronized String obtainString() {
// TODO Auto-generated method stub
if(!ready){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return real.obtainString();
}
}
Real 实现了Data接口,实现了ObtainString()方法
public class Real implements Data {
public Real(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String obtainString() {
// TODO Auto-generated method stub
return "Hello,langSin!!!";
}
}
RequestHandler
public class RequestHandler {
public Data requestData(){
final Future future = new Future();
new Thread(new Runnable() {
@Override
public void run() {
Real real = new Real();
future.setReal(real);
}
}).start();
return future;
}
}
Go
public class Go {
public static void main(String[] args) {
RequestHandler handler = new RequestHandler();
Data data1 = handler.requestData();
Data data2 = handler.requestData();
System.out.println(data1.obtainString());
System.out.println("我在做其他事情");
System.out.println(data2.obtainString());
}
}