浅谈java多线程机制
一、线程和进程
1. 概念的区别:
线程:程序执行的路径
进程:内存中运行的程序
注意:进程是内存中实实在在的内存空间,而线程则是程序运行时的不同路径只在一个内存空间内存在。
2.线程的调度
- 分时调度:所有线程轮流占用CPU的使用权,平均分配使用时间到每个线程。
- 抢占式调度(Java所使用):优先让优先级高的线程使用CPU,如果线程的优先级相同,则随机选择线程。
使用setPriority(int newPriority)更改线程的优先级(!但不是一定的 ,我测试过很多情况并不能根据优先级的设置进行执行)
二、同步与异步(并发与非并发)
1.概念
- 同步是类似排队的额执行→效率低但安全→线程安全
- 异步是同时执行→效率高但不安全→非线程安全
三、实现
→继承自Thread
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
/*for (int i=0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}*/
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
每个线程有用自己的栈空间 但共同享有堆空间
→实现Runnable(常用且推荐使用)
- 创建一个Runnable对象也就是任务
- 创建一个线程,并非配Runnable任务
- 线程.start()执行
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(new MyRunnable());
t1.start();
t2.start();
t3.start();
/*for (int i=0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}*/
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
优点:避免extends Thread时单继承的局限;任务和线程分离 更便于理解思想和程序的健壮性;最重要的是线程池技术只接受Runnable类型的任务
四、线程的Thread类详解
1. sleep()方法
让当前线程睡眠,可传入设定睡眠时间参数(毫秒)
直接用Thread.sleep()
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
五、线程的特点
1. 线程的阻塞:所有消耗时间的操作导致线程在某处等待,就叫做线程的阻塞。
2.线程的中断:
- 一个线程是一条独立的路径,是否中断应该由线程自身来决定 而不应该使用stop()方法强制中断。
- 通常做法是给线程打标记,告诉线程应该中断了,然后线程接收到标记信息后,决定是否中断。
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
t1.interrupt();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("我被通知中断了,执行中断。");
break;
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
六、线程的安全和不安全
经典卖票逻辑
要解决卖票时的逻辑问题 要使用排队的逻辑
1.解决方案1:
使用同步代码块(隐式锁)
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.setName("窗口1");
Thread t2 = new Thread(mr);
t2.setName("窗口2");
Thread t3 = new Thread(mr);
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
int count = 10;
Object o = new Object();
@Override
public void run() {
while(true){
synchronized (o){
if (count>0){
System.out.println("开始卖票");
count--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+count+"张。");
if (count==0){
System.out.println("票已卖光。");
break;
}
}
}
}
}
}
2.解决方案2
使用同步方法(隐式锁)
package com.jiansheng;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.setName("窗口1");
Thread t2 = new Thread(mr);
t2.setName("窗口2");
Thread t3 = new Thread(mr);
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
int count = 10;
Object o = new Object();
@Override
public void run() {
while(true){
if(sale()==false){
break;
}
}
}
public synchronized boolean sale(){
if (count>0){
System.out.println("开始卖票");
count--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+count+"张。");
if (count==0){
System.out.println("票已卖光。");
}
return true;
}
return false;
}
}
3.使用显式锁
推荐使用显式锁,因为思想逻辑上更清晰。
package com.jiansheng;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Jiansheng
*/
public class Demo1 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.setName("窗口1");
Thread t2 = new Thread(mr);
t2.setName("窗口2");
Thread t3 = new Thread(mr);
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
int count = 10;
Object o = new Object();
Lock l =new ReentrantLock();
@Override
public void run() {
while(true){
l.lock();
if (count>0){
System.out.println("开始卖票");
count--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+count+"张。");
if (count==0){
System.out.println("票已卖光。");
break;
}
}
l.unlock();
}
}
}
Jiansheng