首先 理解进程的概念,在操作系统中一个程序的 执行周期就称为一个进程。在最初的DOS操作系统由于其本身只是一个单进程的系统,所以在同一时间段只能执行一个程序,而后来windows可以执行多进程,这样一块资源在同一时间段会有多个进程交替执行,但是在同一个时间点只会有一个时间点去执行。
而线程是在进程上面的进一步划分,线程是比进程更小的单位。Java本身是一个多线程的语言,多线程引用体现在哪里呢?
所谓高并发就是访问的线程 爆高
Java多线程的实现
前提:有一个线程的执行主类
用户----》主类main方法------》多线程主类,我们主要考虑 的就是多线程主类的定义结构,现在有两种方式可以实现这个主类,第一种继承Thread类,第二种是实现Runnable或Callable接口
1.Thread实现
package com.wjx.sayHello;
class myThread extends Thread{
private String name;
public myThread(String name){
this.name=name;
}
@Override
public void run(){
for(int x=0;x<10;x++){
System.out.println(name+",x="+x);
}
}
}
public class TestDemo {
public static void main(String[] args){
myThread m1=new myThread("A");
myThread m2=new myThread("B");
myThread m3=new myThread("C");
m1.start();
m2.start();
m3.start();
}
}
启动多线程public void start(),调用此方法虚拟机会调用run()方法,对象直接调用run()方法没有意义,那为啥呢?看看源码
2.Runnable接口
Thread 和 Runnable 的关系:public class Thread extends Object implements Runnable
package com.wjx.sayHello;
class myThread implements Runnable{
private String name;
public myThread(String name){
this.name=name;
}
@Override
public void run(){
for(int x=0;x<10;x++){
System.out.println(name+",x="+x);
}
}
}
public class TestDemo {
public static void main(String[] args){
myThread m1=new myThread("A");
myThread m2=new myThread("B");
myThread m3=new myThread("C");
// m1.start();
// m2.start();
// m3.start();
new Thread(m1).start();
new Thread(m2).start();
new Thread(m3).start();
}
}
3.通过callable
jdk1.5之后追加了一个开发包Java,util.concurrent,这个开发包主要提供一些高并发的类。
由于Runnable 的run()方法也是线程的主方法,但是没有返回值,当线程执行完成带来一些返回值的时候我们这个时候就需要使用Callable来实现多线程
package com.wjx.sayHello;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class myThread implements Callable<String>{
@Override
public String call() throws Exception {
for(int x=0;x<10;x++){
System.out.println("x="+x);
}
return "票卖完了";
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException{
FutureTask<String> ft=new FutureTask<String>(new myThread());
new Thread(ft).start();
System.out.println(ft.get());
}
}
4.多线程的常用操作方法
(1)线程的命名和取得
package com.wjx.sayHello;
class myThread implements Runnable{
@Override
public void run() {
for(int x=0;x<10;x++){
System.out.println("线程名字是:"+Thread.currentThread().getName()+",x的值是:"+x);
}
}
}
public class TestDemo {
public static void main(String[] args){
myThread mt=new myThread();
//Thread(Runnable target, String name)构造方法,返回name值
new Thread(mt, "Leo").start();//
new Thread(mt).start();//自动命名,比如Thread-0
mt.run();//返回main,主方法本身是一个线程
}
}
(2)线程休眠
public static void sleep(long millis) throws InterruptedException
package com.wjx.sayHello;
class myThread implements Runnable{
@Override
public void run() {
for(int x=0;x<10;x++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程名字是:"+Thread.currentThread().getName()+",x的值是:"+x);
}
}
}
public class TestDemo {
public static void main(String[] args){
myThread mt=new myThread();
//Thread(Runnable target, String name)构造方法,返回name值
new Thread(mt, "Leo").start();//
new Thread(mt).start();//自动命名,比如Thread-0
mt.run();//返回main,主方法本身是一个线程
}
}
3.线程优先级
package com.wjx.sayHello;
class myThread implements Runnable{
@Override
public void run() {
for(int x=0;x<2;x++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程名字是:"+Thread.currentThread().getName()+",x的值是:"+x);
}
}
}
public class TestDemo {
public static void main(String[] args){
//获取主方法的优先级,为NORM_PRIORITY 返回是5
System.out.println(Thread.currentThread().getPriority());
myThread mt=new myThread();
Thread t1=new Thread(mt,"ThreadA");
Thread t2=new Thread(mt,"ThreadB");
Thread t3=new Thread(mt,"ThreadC");
// 设置优先级,只是有可能优先执行,并不一定啊
t1.setPriority(Thread.MIN_PRIORITY); //MIN_PRIORITY 常量1
t2.setPriority(Thread.NORM_PRIORITY);//MIN_PRIORITY 常量5
t3.setPriority(Thread.MAX_PRIORITY);//MIN_PRIORITY 常量10
t1.start();
t2.start();
t3.start();
}
}
4 线程的同步和死锁
同步问题的引出:观察一个不同步的问题
package com.wjx.sayHello;
class myThread implements Runnable{
private int ticket=10;
@Override
public void run() {
for(int x=0;x<20;x++){
if(ticket>0){
try {
Thread.sleep(1000);//延迟暴露问题,同步会造成当几个进程同时判断票数>0都满足的同时,由于延迟,ticket--还没有操作,如果此时其他进来,票数可能小于0
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("售票员名字是:"+Thread.currentThread().getName()+",票数剩余是:"+ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args){
//获取主方法的优先级,为NORM_PRIORITY 返回是5
myThread mt=new myThread();
Thread t1=new Thread(mt,"张三");
Thread t2=new Thread(mt,"李四");
Thread t3=new Thread(mt,"王五");
t1.start();
t2.start();
t3.start();
//执行结果:
// 售票员名字是:张三,票数剩余是:10
// 售票员名字是:李四,票数剩余是:9
// 售票员名字是:王五,票数剩余是:8
// 售票员名字是:张三,票数剩余是:7
// 售票员名字是:李四,票数剩余是:6
// 售票员名字是:王五,票数剩余是:5
// 售票员名字是:张三,票数剩余是:4
// 售票员名字是:李四,票数剩余是:3
// 售票员名字是:王五,票数剩余是:2
// 售票员名字是:张三,票数剩余是:1
// 售票员名字是:李四,票数剩余是:0
// 售票员名字是:王五,票数剩余是:-1
//结论:不同步数据的访问是不安全的
}
}
如何处理:同步处理:所有线程不是一起进入方法,而是按照顺序一步一步进入。当一个程序进入一个方法的时候,上把锁,不解锁,其他进程无法进入,我们使用Synchronized来处理,有两种方式,同步代码块,同步方法。同步虽然保证数据的完整性,但是访问速度变慢了。
1...同步代码块(必须要设置一个被锁定的对象,一般锁定当前对象)
package com.wjx.sayHello;
class myThread implements Runnable{
private int ticket=10;
@Override
public void run() {
for(int x=0;x<20;x++){
synchronized(this){
if(ticket>0){
try {
Thread.sleep(1000);//延迟暴露问题,同步会造成当几个进程同时判断票数>0都满足的同时,由于延迟,ticket--还没有操作,如果此时其他进来,票数可能小于0
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("售票员名字是:"+Thread.currentThread().getName()+",票数剩余是:"+ticket--);
}
}
}
}
}
public class TestDemo {
public static void main(String[] args){
//获取主方法的优先级,为NORM_PRIORITY 返回是5
myThread mt=new myThread();
Thread t1=new Thread(mt,"张三");
Thread t2=new Thread(mt,"李四");
Thread t3=new Thread(mt,"王五");
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(Thread.NORM_PRIORITY);
t1.start();
t2.start();
t3.start();
//执行结果
// 售票员名字是:王五,票数剩余是:10
// 售票员名字是:王五,票数剩余是:9
// 售票员名字是:王五,票数剩余是:8
// 售票员名字是:张三,票数剩余是:7
// 售票员名字是:李四,票数剩余是:6
// 售票员名字是:李四,票数剩余是:5
// 售票员名字是:李四,票数剩余是:4
// 售票员名字是:李四,票数剩余是:3
// 售票员名字是:李四,票数剩余是:2
// 售票员名字是:李四,票数剩余是:1
}
}
2...同步方法
package com.wjx.sayHello;
class myThread implements Runnable{
private int ticket=10;
@Override
public void run() {
sale();
}
public synchronized void sale(){
for(int x=0;x<20;x++){
if(ticket>0){
try {
Thread.sleep(1000);//延迟暴露问题,同步会造成当几个进程同时判断票数>0都满足的同时,由于延迟,ticket--还没有操作,如果此时其他进来,票数可能小于0
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("售票员名字是:"+Thread.currentThread().getName()+",票数剩余是:"+ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args){
//获取主方法的优先级,为NORM_PRIORITY 返回是5
myThread mt=new myThread();
Thread t1=new Thread(mt,"张三");
Thread t2=new Thread(mt,"李四");
Thread t3=new Thread(mt,"王五");
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(Thread.NORM_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
3死锁(同步带来的问题)
模拟一个死锁 ,下面是一个死锁程序,程序造成死锁
package com.wjx.sayHello;
class Pen{
public synchronized void get(){
System.out.println("获取笔记本");
}
public synchronized void getOther(Book book){
System.out.println("获取其他,book");
book.get();
}
}
class Book{
public synchronized void get(){
System.out.println("获取笔");
}
public synchronized void getOther(Pen pen){
System.out.println("获取其他,pen");
pen.get();
}
}
public class DeadLock implements Runnable{
Pen pen =new Pen();
Book book=new Book();
public static void main(String[] args) {
new DeadLock();
}
public DeadLock(){
new Thread(this).start();
pen.getOther(book);
}
@Override
public void run() {
book.getOther(pen);
}
}