1.进程和线程
进程和线程都是一个控制流程。
一个进程通常对应于一个程序。
一个程序可以由多个不同的线程构成。
一个进程就是一个应用程序
一个应用程序面对多个用户则多个进程
一个进程则多个线程
多个线程共享资源且携带数据操作
多进程资源隔离
2.多线程的核心
多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。
3.操作系统的多任务–以非常小的时间间隔交替执行
进程:正在进行的程序
现在使用的操作系统都是多任务的,即能够 同时 执行多个应用程序。实际是操作系统负责对CPU等设备资源进行分配和管理,虽然这些设备某一时刻只能做一件事情,但以非常小的时间间隔交替执行多个程序,就给人同时执行多个程序的感觉
4.native 修饰的方法
Native Method 调用java外的程序包,其中包含
一个Native Method就是一个java调用非java代码的接口
一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。
5.Thread
一个Thread类的对象代表一个线程,而且只能代表一个线程。
通过Thread类和它定义的对象,我们能获得当前线程对象、获取某一线程的名称、可以实现控制线程暂停一段时间等功能
创建线程的两种方式
共同点都是自定义线程类重写run()方法 并且通过 new Thread (线程对象的实例).start() 方法启动线程和创建一个实例线程启动运行run()方法,真正实现多线程的方式实现
1.普遍采用实现Runnable接口的方式
public class MyThreadImp implements Runnable {
@Override
public void run() {
System.out.println("执行实现Runable接口的MyThreadImp的类中run()方法");
}
public static void main(String[] args) {
MyThreadImp myThreadImp = new MyThreadImp();
Thread thread =new Thread(myThreadImp);
thread.start();//执行实现Runable接口的MyThreadImp的类中run()方法
}
}
2.继承Thread方式
public class MyThread extends Thread{
//等待线程调用
public void run (){
System.out.println("执行了Mythread的run() 方法");
System.out.println(currentThread());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();// 执行了Mythread的run() 方法
}
}
6.自定义线程用 new Thread(Runnable target) 启动源码分析
6.1-new Thread(myThread)
主要
1启动线程;
thread.start();
private native void start0(); 调用外部接口启动线程 可能涉及到 硬件或者动态链接库函数等待
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
6.2对实例 myThread 创建线程 并给 Thread类 成员变量 target 赋予当前对象实例
创建实例的线程(如果必要可以给线程等级,权限)
/* What will be run. */ 运行哪一个对象实例
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//创建线程
Thread parent = currentThread()
。。。。。。
// 成员变量 target 赋予当前对象实例
this.target = target;
//获取守护进程
this.daemon = parent.isDaemon();
//获取进程权限
this.priority = parent.getPriority();
。。。。。。
//省略部分代码
。。。。。。
/* Set thread ID */
//获取下个线程id
tid = nextThreadID();
}
7.实现接口Runnable和继承Thread类的区别
①处理和共享同一资源对多个相同的程序代码的线程
②避免由于java单继承特征带来的局限性
③增强代码的健壮性和灵活性可以多个线程共享,代码和数据是独立的
8.启动线程通过start()和 run () 区别
start方法:
通过该方法启动线程的同时也创建了一个线程.真正实现了多线程。并不等待run方法法中的代码执行完毕,就可以接着执行下面的代码。此时start0的这个线程处于就绪状态.等待到CPU的时间片后就会执行其史的run方法。.这个run方法 含了要执行的这个线程的内容, run()方法运行结束,此线程也就终止了。
run方法:
通过run方法启动线程其实就是调用一个类中的方法,当作普通的方法的方式调用。并没有创建一个线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码执行完毕,才会继续执行下面的代码,这样就没有达到写线程的目的。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
当一个start()线程启动的时候,它的状态〈threadStatus)被设置为О(即线程为NEW状态),如果不为0,则抛出llegalThreadStateException异常。
正常的话,将该线程加入线程组,最后尝试调用start0方法,而start0方法是私有的native方法(Native Method是一个java调用非java代码的接口)。
start调用后,线程出于就绪状态RUNNABLE,等分配到cpu时间片时系统会调用thread的run()方法,而这个方法其实只是调用runnable里面自己实现的run()方法。
public class Test7Thread extends Thread{
//
public void run (){
System.out.println("执行了Test7Thread");
System.out.println(currentThread());
}
public static void main(String[] args) {
Test7Thread test7Thread = new Test7Thread();
test7Thread.start();//执行了Test7Thread
}
}
public class test5 {
public static void main(String[] args) {
Thread thread = new Thread(){
public void run(){
pong();
}
};
thread.start();//启动线程start方法 启动run方法
thread.run();//启动run方法 启动线程start方法
System.out.println("启动线程start方法");
}
static void pong (){
System.out.println("启动run方法");
}
}
9.Thread类target
target - 其 run 方法被调用的对象。
首先run方法是接口 Runnable 中,而Thread 实现此接口并重写run方法
与此同时创建创建新执行线程有两种方法。
一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:
不管是继承还是实现接口的都是调用了run() 方法 ,谁去调用的??是创建的类的对象调用,
故 target - 其 run 方法被调用的对象。
Thread类中
@Override
public void run() {
if (target != null) {
target.run();
}
}
10.1线程之一经典的银行转账线程问题
有一个银行账户,还有余额1100元,现在A通过银行卡从中取1000元,而同时另外一个人B通过存折也从这个账户中取1000元。取钱之前,要首先进行判断:如果账户中的余额大于要取的金额,则可以执行取款操作,否则,将拒绝取款。
我们假定有两个线程来分别从银行卡和存折进行取款操作,当A线程执行完判断语句后,获得了当前账户中的余额数(1000元),因为余额大于取款金额,所以准备执行取钱操作(从账户中减去1000元),但此时它被线程B打断,然后,线程B根据余额,从中取出1000元,然后,将账户里面的余额减去1000元,然后,返回执行线程A的动作,这个线程将从上次中断的地方开始执行:也就是说,它将不再判断账户中的余额,而是直接将上次中断之前获得的余额减去1000。此时,经过两次的取款操作,账户中的余额为100元,从账面上来看,银行支出了1000元,但实际上,银行支出了2000元。
10.2线程之二 经典的买火车票问题
方法一
=========继承Thread==========模拟4个售票窗口共同卖100张火车票的程序=============
public class TicketThread {
public static void main(String[] args){
// 执行继承Thread的线程
new MyThread().start();//售票窗口1
new MyThread().start();//售票窗口2
new MyThread().start();//售票窗口3
new MyThread().start();//售票窗口4
}
}
class MyThread extends Thread {
// 车票数量
private int tickets = 100;
public void run() {
while (tickets > 0) {
System.out.println(this.getName() + " 卖出第 " + tickets-- + "张火车票.");
}
}
}
方法二
==========实现Runnable接口=========模拟4个售票窗口共同卖100张火车票的程序=============
public class TicketRunnable {
public static void main(String[] args) {
//实现Runnable接口实现类
myRunnable myR = new myRunnable();
new Thread(myR).start();
new Thread(myR).start();
new Thread(myR).start();
new Thread(myR).start();
}
}
class myRunnable implements Runnable {
//火车票数量
private int tickets = 100;
public void run() {
while (tickets > 0) {
System.out.println(Thread.currentThread().getName()+"卖出第["+(tickets--) +"]张火车票.");
}
}
}
11工作中,几乎所有的多线程应用都用实现Runnable这种方式
把虚拟CPU(线程)同程序的代码、数据有效的分离
健壮、灵活
编写简单,可以直接操纵线程,无需使用Thread.currentThread()。
12关于线程安全问题
多线程共享数据时的问题
在售票程序中,System.out.println(Thread.currentThread().getName()+“卖出第[”+(tickets–) +“]张火车票.”);这段代码执行时候,极有可能出现一种意外。就是同一张票可能会被打印两次或多次,也可能出现打印0或负数的情况。
假设:当tickets的值为1的时候,线程1刚执行完if(tickets>0),正准备执行下面的代码,这时,操作系统将CPU切换到了线程2上执行,此时tickets的值仍是1,线程2执行完以下代码,CPU切回线程1,
线程1不再执行if(tickets>0),继续往下执行,此时意外出现。
可以使用sleep()来制造线程中这种切换的错误。
这种错误也就是我们常说的"线程安全
解决之道–思想–线程同步
多个线程都想对同一个资源或代码块的时,此时此刻只能被单独一个线程执行完毕再释放出去待下一线程继续使用这个代码块,这就是线程同步。
比如:一张火车票只有可能出售给一位旅客,下一个旅客只能等待下一个线程继续操作申请另一张票
解决之道–具体实现
将具有原子性的代码放入synchronize语句内,形成同步代码块。就可以保证线程安全了。
每个对象都有一个标志位(锁旗标),该标志位有两个状态0、1,其开始状态为1,当执行synchronized(Object)语句后,Object对象的标志位变为0状态,直到执行完整个synchronized语句中的代码块后才又回到1状态。一个线程执行到synchronized(Object)的时候,先检查Object对象的标志位,0表示有线程在执行,这个线程将暂时阻塞,让出CPU资源,直到另外线程执行完有关代码,将Object对象状态恢复到1状态,这个阻塞才被取消,然后线程开始执行,同时将Object对象的标志位变为0,防止其他线程再进入有关的同步代码块中。
当线程执行到synchronized的时候,如果得不到锁标记,这个线程会被加入到一个与该对象的锁标记相关连的等待线程池当中,等待锁标记。当线程执行完同步代码,就会释放锁标记。一个刚释放了锁标记的线程可以马上再次获得锁标记,当同步块遇到break或抛出exception时,线程也会释放锁标记。
wait、notify、notifyAll这三个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁旗标,这样,notify只能唤醒同一对象监视器中调用wait的线程。
所以,如果确定程序没有安全性的问题,就没有必要使用同步。
ArrayList Vector(线程同步)
同步是以牺牲程序的性能为代价的。
所以,如果确定程序没有安全性的问题,就没有必要使用同步。
synchronized还可以作用在方法上面,用来实现线程的同步
同步块越短越好,锁力度过粗容易导致同步严重,力度过细容易发生死锁问题
Synchronized(this){--粗
System.out.println("hello");
System.out.println("world");
}
--------------------------------
Synchronized(this){--细
System.out.println("hello");
}
System.out.println("world");
--------------------------------
如果是静态方法则没有实例,则锁类对象this.getClass
一个类不管有多少个实例,只有一个类对象.
Synchronized(this.getClass()){}
13.死锁
是指两个线程,都相互等待对方释放lock
是不可测知或避开的
应采取措施避免死锁的出现
14.线程间的通信
Object 类定义了 wait()、notify() 和 notifyAll() 方法。可以让线程相互通知事件的发生。要执行这些方法,必须拥有相关对象的锁。
wait() 会让调用线程休眠,直到用 Thread.interrupt() 中断它 或者wait经过了指定的时间 或者另一个线程用 notify() 或 notifyAll() 唤醒它。
当对某个对象调用 notify() 时,如果有任何线程正在通过 wait() 等待该对象,那么就会唤醒其中一个线程。当对某个对象调用 notifyAll() 时,会唤醒所有正在等待该对象的线程。
wait、notify、notifyAll这三个方法只能在synchronized方法中调用
,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁旗标,这样,notify只能唤醒同一对象监视器中调用wait的线程。
package com.thread;
class Product{
private String name;
private String price;
private int count;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setPrice(String price){
this.price = price;
}
public String getPrice(){
return this.price;
}
public void setCount(int count){
this.count = count;
}
public int getCount(){
return this.count;
}
public String toString(){
return "产品名称:"+this.name+" 价格:"+this.price+" 数量:"+this.count;
}
private boolean flag = false;
public synchronized void put(String name,String price){
try{
if(flag){
//如果有产品就不继续生产
wait();
}
System.out.print("***生产者开始生产-->");
this.setName(name);
this.setPrice(price);
//生产产品数量加1
this.setCount(this.getCount()+1);
System.out.println(this);
flag = true;//生产完毕更改标志
notify();//通知消费者消费
}catch(Exception e){
e.printStackTrace();
}
}
public synchronized void get(){
try{
if(!flag){
wait();
}
System.out.print("--消费者开始消费产品-->");
//消费一个产品数量减 1
this.setCount(this.getCount()-1);
System.out.println(this);
flag = false;//消费完,更改标志;
notify();//通知生产者继续生产
}catch(Exception e){
e.printStackTrace();
}
}
}
class Producter implements Runnable{
private Product p;
public Producter(Product p){
this.p = p;
}
public void run(){
int i=0,n=0;
while(n<5){
n++;
if(i==0){
p.put("包子","1");
}else{
p.put("馒头","2");
}
//实现交替生产效果
i = (i+1)%2;
}
}
}
class Consumer implements Runnable{
private Product p;
public Consumer(Product p){
this.p = p;
}
public void run(){
int n = 0;
while(n<5){
n++;
p.get();
}
}
}
public class TestThread{
public static void main(String[] args){
Product p = new Product();
new Thread(new Producter(p)).start();
new Thread(new Consumer(p)).start();
}
}
15.同步实现方式–线程的状态
synchronized,wait和notify,notityAll。文章来源:https://www.toymoban.com/news/detail-430795.html
文章来源地址https://www.toymoban.com/news/detail-430795.html
到了这里,关于Java进程线程介绍创建和执行销毁并理解线程安全和线程池 Native Method的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!