</pre><span style="white-space:pre"></span><p>进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)</p><p><span style="white-space:pre"></span>线程:<strong>进程中的一段代码</strong>,一个进程中可以有多段代码。本身不拥有资源(共享所在进程的资源)在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。区别: 1、是否占有资源问题 2、创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。</p><p> 3、进程为重量级组件,线程为轻量级组件</p><p> 多进程: 在操作系统中能同时运行多个任务(程序)</p><p> 多线程: 在同一应用程序中有多个功能流同时执行 </p>多线程(多个线程同时运行)程序的主要优点<span style="white-space:pre"></span>可以减轻系统性能方面的瓶颈,因为可以并行操作;<p><span style="white-space:pre"></span>提高CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性;另一方面,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。</p><p>创建一个线程有两个办法:继承Thread类或者实现Runnable接口。</p><p>第一种:</p><p></p><pre name="code" class="java">class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<40;i++)
System.out.println(i);
}
}
public class Main {
public static void main(String[] args) {
MyThread m=new MyThread("myThread");
m.start();
System.out.println("main");
}
}
第二种:
//在java中要想实现多线程,一是继续Thread类,一是实现Runable接口。
class MyDemo implements Runnable{
@Override
public void run() {
for(int i=0;i<40;i++)
System.out.println("run----"+i);
}
}
public class Deamo {
public static void main(String[] args) {
Thread t=new Thread(new MyDemo()); //创建线程
t.start(); //启动线程,线程进入就绪状态,等待调用
//t.run(); //不需要调用,线程被调用时就会执行
for(int i=0;i<40;i++)
System.out.println("main----"+i);//运行结果可能是main/run交替执行
}
}
线程的状态:
新建状态
建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。
就绪状态
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,等待系统为其分配CPU。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
死亡状态
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。
当一个线程进入死亡状态以后,就不能再回到其它状态了。通过查API可以看到stop方法和destory方法已经过时了,所以不能再用,那要怎样做才能强制的销毁一个线程呢?
1、在run方法中执行return 线程同样结束
2、可以在while循环的条件中设定一个标志位,当它等于false的时候,while循环就不在运行,这样线程也就结束了。代码为实现的代码示例:
public class MyRunable implements Runnable {
private boolean isStop; //线程是否停止的标志位
public void run() {
while (!isStop)
System.out.println("invoke MyRunable run method");
}
public void stop(){ //终止线程
isStop=true;
}
public static void main(String[] args) {
MyRunable myRunable=new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
e.printStackTrace();
}
myRunable.stop(); //正确的停止线程的方法
}
}
阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。
class MyDemo implements Runnable{
@Override
public void run() {
for(int i=0;i<40;i++){
System.out.println("run----"+i);
}
}
}
public class Deamo {
public static void main(String[] args) {
Thread t=new Thread(new MyDemo());
t.start();
Scanner sc=new Scanner(System.in);
sc.next(); //等待输入,主线程进入阻塞
}
}
上面程序先执行run中的内容,最后以输入一个字符串结束
线程的同步
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题,为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。在Java中,由于对多线程的支持,对同步的控制主要通过以下几个方法,synchronized,和wait(),notify()和notifyAll()
每个java对象都有一把锁, 当有多个线程同时访问共享资源的时候, 需要Synchronize 来控制安全性, synchronize 分 synchronize 方法 和synchronize块,只有其中的内容执行完了,其它线程才可以执行,原子性
下面看一个例子,四个窗口同时卖100张票,tickets为共享数据
class MyDemo implements Runnable{
private int tickets=100;
@Override
public void run() {
while(tickets>0){
try {
Thread.sleep(1000); ///-----------为了使用错误更明显,更容易出现
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":第 "+(tickets--)+" 张票");
}
}
}
public class Deamo {
public static void main(String[] args) {
MyDemo my=new MyDemo();
Thread t=new Thread(my),t1=new Thread(my),t2=new Thread(my),t3=new Thread(my);
t.start(); t1.start();t2.start();t3.start();
}
}
Thread-0:第 3 张票
Thread-1:第 2 张票
Thread-3:第 1 张票
Thread-1:第 0 张票
Thread-0:第 -2 张票
Thread-2:第 -1 张票
这是其中最后几张的卖出,出现了负数,也就是说,当其中线程1 while成立时,在循环内部某个时期,另一个获得了执行,而线程1再获得执行时可能出错
同步代码块:synchronized (对象) { ----需要同步的内容,一般为共享资源的使用-- } 对象可以是任何对象,但几个线程必须为同一个线程
所以上例改为:
class MyDemo implements Runnable {
private int tickets = 100;
Object obj=new Object();
//以上是程序中共享的资源
public void run() { //Object obj=new Object();不能放在这里面,因为每个线程都有自己的run方法 ,如果这里,锁的对象obj就不是同一个了,也就达不到同步效果
synchronized (obj) { //没有锁的线程即使获得了cpu执行权,也进不去
while (tickets > 0) {
try {
Thread.sleep(100); // /-----------为了使用错误更明显,更容易出现
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":第 "
+ (tickets--) + " 张票");
}
}
}
}
public class Deamo {
public static void main(String[] args) {
MyDemo my = new MyDemo();
Thread t = new Thread(my), t1 = new Thread(my), t2 = new Thread(my), t3 = new Thread(my);
t.start();t1.start();t2.start();t3.start();//启动四个线程同时进行
}
}
同步方法,即在方法前加synchronized 即可 public synchronized void run() { }
synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问
同步函数调用的哪一个锁呢? 函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this
以下为验证:
class Ticket implements Runnable {
private int ticket = 100;
Object obj = new Object();
boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (this) // 两个线程为同一个锁:安全 换成obj的话为两个锁:不完全
{
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()
+ "---sale " + ticket--);
}
}
}
} else
while (true)
show();
}
public synchronized void show() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "----show---"
+ ticket--);
}
}
}
class Test {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);
} catch (Exception e) {
}
t.flag = false;
t2.start();
}
}
如果同步函数是静态函数,使用的锁是什么呢?不在是this,因为静态方法中也不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class 该对象的类型是Class,静态的同步方法,使用的锁是该方法所在类的字节码文件对象:类名.class,静态方法内部同步代码块也只能用 类名.class 对象
验证:
class Ticket implements Runnable {
private static int ticket = 100;
Object obj = new Object();
boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (Ticket.class) // 两个线程为同一个锁:安全 换成obj的话为两个锁:不完全
{
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()
+ "---sale " + ticket--);
}
}
}
} else
while (true)
show();
}
public static synchronized void show() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "----show---"
+ ticket--);
}
}
}
class Test {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);
} catch (Exception e) {
}
t.flag = false;
t2.start();
}
}
下面是多线程在单例设计模式中的应用:
单例模式分为:饿汉式,懒汉式
饿汉式:不存在多线程问题
class Single{
private static final Single s=new Single();
public static Single getS() {
return s;
}
}
懒汉式:
class Single { // 实例延迟加载了
private static Single s = null; // 注意此处不能用final了
public static Single getS() {// 由于多线程问题,产生的实例可能不是一个,所以要用同步机制
if (s == null) {
synchronized (Single.class) {//此处静方法只能是Single.class
return new Single();
}
}
return s;
}
}
线程优先级:
与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。
Java线程的优先级是一个整数,其取值范围是1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。Thread源代码里对NORM_PRIORITY (数值为5) 的注释是“线程默认的优先级”