进程和线程:
进程是指一个内存中运行中的应用程序,每个进程都有自己独立的一块内存空间。
一个进程至少一个线程,为了提高效率,一个进程中开启多个执行任务,即多线程。
开启新的进程很耗费内存和性能。
进程和线程的区别:
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小,相互可以影响。
从微观角度线程也有先后。
Java程序的进程(java的一个程序运行在系统中)至少包含主线程和垃圾回收线程。
线程调度:
计算机通常只有一个cpu,在任意时刻只能执行一条计算机指令,每一个进程只有获得cpu的使用权才能执行指令。从宏观上讲,就是各个进程轮流获得cpu使用权,分别执行各自的任务。
Jvm采用抢占式调度,没有采用分时调度,因此可能造成多线程执行结果的随机性。
多线程的优势:
进程之间不能共享内存,线程之间共享内存(堆内存)
系统创建进程时需要为该进程重新分配系统资源,因此多任务并发时,多线程的效率更高。
Java语言本身内置多线程功能的支持,而不是单纯作为底层系统的调度方式,从而简化了多线程编程。
CPU线程数也影响了系统的速率,买电脑可以看看。
宽带带宽以单位(bit)计算,而下载速度是以字节(Byte)为单位,一个字节等于8个bit。所以你是1M带宽,那要先除以8,而且要低一点。
多线程抢占资源更快,更多。
Java操作进程
在java代码中如何运行一个进程(简单讲解,获取继承中的数据)
**Runtime类中的exec方法
ProcessBuilder的start方法**
import java.io.IOException;
public class Demo5 {
public static void main(String[] args) throws IOException {
//是同Runtime类的exec方法
Runtime runtime = Runtime.getRuntime();
runtime.exec("notepad");
//方式二:ProcessBuilder的start()方法
ProcessBuilder pb = new ProcessBuilder("notepad");
pb.start();
}
}
创建和启动线程,传统有两种方式:
继承手段:
实现接口的手段:
线程类(java.Lang.Thread)Thread类和Thread子类才能称之为线程类
别忘了主线程main
方式1:继承Thread类
定义一个类A继承于java.lang.Thread类
在类A中覆盖Thread类中的run方法
我们在run方法中填写需要执行的操作—run方法里的线程执行体
在main方法(线程)中,创建线程对象并启动线程
创建线程类 A类 th = new A类();
调用线程对象的start方法: th.start();
注意:千万不要调用run方法,如果调用润方法好比对象调用方法,依然还是只有一个线程,并没有启动新的线程
例子:边唱歌边玩游戏
//播放音乐的线程类
class MusicThread extends java.lang.Thread{
public void run() {
for(int i = 0; i < 50 ; i ++) {
System.out.println("播放音乐.."+ i);
}
}
}
public class Demo6 {
public static void main(String[] args) {
//主线程:运行游戏
for(int i = 0; i < 50 ; i ++ ) {
System.out.println("打游戏.." + i);
if(i == 10) {
//创建线程对象,并启动线程
MusicThread t = new MusicThread();
t.start();
}
}
}
}
结果有不可预知性,线程只能启动一次
实现接口Runnable接口
定义一个类A实现一个接口叫做Runnable接口
在A类中覆盖Runnable接口中的run方法
我们在run方法中编写并执行操作—run方法中的线程执行体
在main方法(线程)中,创建线程对象,并启动线程
创建线程类对象 Thread t = new Thread();
调用线程对象的start方法 t.start();
//播放音乐的一个类
class music implements java.lang.Runnable{
public void run() {
for(int i = 0; i < 50 ; i ++) {
System.out.println("播放音乐.."+ i);
}
}
}
public class Demo7 {
public static void main(String[] args) {
//主线程:运行游戏
for(int i = 0; i < 50 ; i ++ ) {
System.out.println("打游戏.." + i);
if(i == 10) {
//创建线程对象,并启动线程
music tar = new music();
Thread t = new Thread(tar);
t.start();
}
}
}
}
**使用匿名内部类创建线程
匿名内部类
只适用于使用一次的情**况
public class Demo8 {
public static void main(String[] args) {
//主线程,运行游戏
for(int i = 0; i < 50; i++) {
System.out.println("打游戏.."+ i);
if(i == 10) {
//创建线程对象,并启动线程
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println("打豆豆.."+ i);
}
}
}).start();
}
}
}
}
吃苹果案例
有五十个苹果,让三个人吃完苹果,三个同学代号a,b,c,此时要使用多线程技术来实现这个案例,a,b,c可以同时吃苹果,看谁吃的快?
分析:可以定义三个线程对象,并启动线程。
每一个同学吃苹果的时候,先显示自己拿到手上苹果的编号,在吃掉苹果,意味着苹果总数少一个。
**方式1:使用Thread类方式实现
方式2:使用Runnable接口实现**
方式1:-//用继承方式实现让3个同学吃50苹果的方法
class Person extends Thread {
private int num = 50;
public Person(String name) {
super(name);
}
public void run() {
for(int i = 0; i < 50; i++) {
if(num > 0 ) {
System.out.println(super.getName()+"编号为"+num--+"被吃");
}
}
}
}
public class Demo9 {
public static void main(String[] args) {
//创建三个线程:三个同学吃苹果
new Person("a").start();;
new Person("b").start();;
new Person("c").start();;
}
}
这时候三个每个吃50,各自拥有一个num=50这是不可行的
方式2://使用实现接口方式来实现三人吃苹果比赛
class Person implements Runnable {
private int num = 50;
//static Thread currentThread()
//返回当前线程的引用
public void run() {
for(int i = 0; i < 50; i++) {
if(num > 0 ) {
System.out.println(Thread.currentThread().getName()+"编号为"+num--+"被吃");
}
}
}
}
public class Demo10 {
public static void main(String[] args) {
Person a = new Person();
new Thread(a,"a").start();
new Thread(a,"b").start();
new Thread(a,"c").start();
}
}
在使用接口时,三个人一共吃了50个苹果,因为三个线程共享了一个对象Person,而一个Person中只有50苹果
**分析继承方式和实现方式的优缺点
继承方式:1)面向对象:java中类是单继承的,如果继承了Thread类,该类就不能再有其他的父类了
2)从操作上说继承方式更简单,获取线程名字也简单
3)不能实现共享资源,除非加static
实现方式:1)面向对象:java中类可以多实现接口,此时该类可以继承其他类,并且还可以实现其他接口
2)从操作上说实现方式复杂点,名字获取需要Thread.currentThread()来获取当前线程的引用
3)可以做到共享资源
同步代码块:**
语法synchronlzed(同步锁){
需要同步操作的代码
}
同步锁:为了保证每个线程都能正常执行原子操作,java引入了线程同步机制
同步监听对象/同步锁/同步监听器/互斥锁
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
Java程序运行使用任何对象作为同步监听对象,但是一般我们把当前发生并发访问的共同资源作为同步监听对象。
在任何时候最多允许一个线程拥有同步锁,谁拿到就进去,其他人等着。
//用继承方式实现让3个同学吃50苹果的方法
class Person1 implements Runnable {
private static int num = 50;
public void run() {
//同步代码块
for(int i = 0; i < 50; i++) {
synchronized (this) {
if(num > 0 ) {
System.out.println(Thread.currentThread().getName()+"编号为"+num+"被吃");
}
//模拟网络延迟
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
num--;
}
}
}
}
public class Demo11 {
public static void main(String[] args) {
//创建三个线程:三个同学吃苹果
Person1 shh = new Person1();
new Thread(shh,"aa").start();;
new Thread(shh,"bb").start();;
new Thread(shh,"cc").start();;
}
}
同步方法:
使用synchronized修饰的方法就是同步方法,保证了a调用这个方法,其他等着,a结束,其他人才有机会
Synchronized public void dowork() {
}
此时同步锁是谁?????对于非static方法,同步锁就是this
对于static方法,我们使用当前方法所在类的字节码对象。(Apple.class)
//用继承方式实现让3个同学吃50苹果的方法
class Person4 implements Runnable {
private static int num = 50;
synchronized public void run() {
//同步代码块
for(int i = 0; i < 50; i++) {
if(num > 0 ) {
System.out.println(Thread.currentThread().getName()+"编号为"+num+"被吃");
}
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
num--;
}
}
}
public class Demo12 {
public static void main(String[] args) {
//创建三个线程:三个同学吃苹果
Person4 shh = new Person4();
new Thread(shh,"aa").start();;
new Thread(shh,"bb").start();;
new Thread(shh,"cc").start();;
}
}
被一个人吃完了,synchronized不可以修饰run方法,修饰之后,某一个线程就执行完了所有任务,达不到多线程的效果
解决方法:吧需要同步操作的代码定义在新的方法中并用synchronized修饰,run中调用
//用继承方式实现让3个同学吃50苹果的方法
class Person4 implements Runnable {
private static int num = 50;
public void run() {
//同步代码块
for(int i = 0; i < 50; i++) {
test();
}
}
synchronized private void test() {
if(num > 0 ) {
System.out.println(Thread.currentThread().getName()+"编号为"+num+"被吃");
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
num--;
}
}
}
public class Demo12 {
public static void main(String[] args) {
//创建三个线程:三个同学吃苹果
Person4 shh = new Person4();
new Thread(shh,"aa").start();;
new Thread(shh,"bb").start();;
new Thread(shh,"cc").start();;
}
}
Synchronized的好与坏:
好处:保证了多线程并发访问时的同步操作避免了线程的安全性问题
坏处:使用这个的性能比不用要低
尽量减小synchronized的作用域
StringBuilder和StringBuffer区别:
StringBuffer都加了synchronized
说说ArrayList和Vector的区别或者HashMap和Hashtable的区别
也是synchronized的区别