一、线程基本概述

1.1、进程和线程

进程:一个应用程序一般都是一个进程,正在进行的程序

每一个进程最少都有一个线程,都有一个执行顺序,该顺序是一个执行路径或者一个控制单元

线程:进程中一个独立的控制单元,线程控制着进程的执行。

windows中的任务管理器,可以查看进程,linux下通过ps命令

线程是进程的最小单位

线程依赖于进程

线程随着进程的创建和创建,随着进程的结束而消亡

如迅雷:可以同时开启多个下载,就是多线程

多个程序同时执行,时CPU在很快的切换,看上去是同时执行,实际上是在CPU在切换执行。

多线程存在的意义:可以让程序中的内容同时执行。

二、继承线程的创建

2.1、继承Thread,重写run方法

package com.pb.thread.demo1;

/**
*
* @author Denny
* 继承Thread 类并重写run方法来创建线程类
*
*/
public class MyThread extends Thread { private int count; /*
* 重写run 方法
* @see java.lang.Thread#run()
*/
@Override
public void run(){
/*
* 要运行的代码块或者方法,在这里调用,或者直接写在这里
*/
while(count<10){
count++;
System.out.println("My thread run方法启动了"+count);
}
}
public static void main(String[] args) {
//声明线程类
MyThread mt=new MyThread();
//启动线程,JVM调用run方法
mt.start();
//主线程
for(int x=0;x<10;x++){
System.out.println("main thread"+x);
}
} }

多个线程都在抢占CPU的执行权,谁抢到,谁就执行

在某一时刻,只能有一个程序在运行

CPU在做着快速切换,以达到看上去是同时运行的效果。

多线程一个特性:随机性,谁抢到谁执行.

三、线程的运行和线程状态

3.1、调用start()方法

使该线程开始执行,Java虚拟机调用该线程的run()方法

为什么要重写run方法.

Thread类用于描述线程。

这个类定义一个功能,用于存储线程要运行的代码。该存储功能就是run方法.

也就是说,Thread类中的run方法是用于存储线程要运行的代码

如果直接调用run()方法,就是调用对象的普通方法一样.,仅仅是对象调用方法.线程创建了并没有运行。

只有start()才可以运行线程。

3.2、线程状态

新生:创建线程 new Thread()或者子类

运行:正在运行的线程,调用start()方法

冻结:已经创建的线程,但非运行状态的线程。sleep(long 毫秒), wait()线程等待,直到notify()唤醒才可以继续运行

临时状态阻塞:具备运行资格,但没有执行权,就是还没有被CPU切换到.

消亡:线程死亡stop(),或者线程运行完成。

四、获取线程对象

4.1、获取当前线程对象和名称

继承thread类实现线程,局部变量都有单独的一份不能共享数据

package com.pb.thread.demo1;

/**
*
* @author Denny
* 继承Thread 类并重写run方法来创建线程类
*Thread.currentThread(),获取当前线程对象
*继承thread类实现线程,局部变量都有单独的一份不能共享数据
*/
public class MyThread extends Thread { public MyThread(String name){
super(name);
}
/*
* 重写run 方法
* @see java.lang.Thread#run()
*/
@Override
public void run(){
/*
* 要运行的代码块或者方法,在这里调用,或者直接写在这里
*/
for(int x=0;x<10;x++){ System.out.println("My thread run的名字:"+Thread.currentThread().getName()+","+x);
}
}
public static void main(String[] args) {
//声明线程类
MyThread mt1=new MyThread("mt1");
// mt1.setName("张三"); 设置线程名称
MyThread mt2=new MyThread("mt2");
//启动线程,JVM调用run方法
mt1.start();
mt2.start();
//主线程
for(int x=0;x<10;x++){
System.out.println(currentThread().getName()+","+x);
}
} }

示例:

五、接口实现线程

5.1、实现Runnable接口

重写run方法()

实现接口的方式最大的好处是可以共享数据

示例:多窗口同时售票,

package com.pb.thread.demo1;

/**
*
* @author Administrator 多个窗口同时卖票
*
*/
public class Ticket implements Runnable { private int tick = 100; public void run() { while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName()+"卖:" + tick--);
}else{
break;
}
}
}
public static void main(String[] args) {
//声明线程类
Ticket ticket=new Ticket();
//创建线程对象,并将类做为参数
Thread t1=new Thread(ticket);
t1.setName("一号窗口,");
Thread t2=new Thread(ticket);
t2.setName("二号窗口,");
Thread t3=new Thread(ticket);
t3.setName("三号窗口,");
Thread t4=new Thread(ticket);
t4.setName("四号窗口,");
t1.start();
t2.start();
t3.start();
t4.start(); } }

结果:

 一号窗口,卖:100
一号窗口,卖:98
一号窗口,卖:97
一号窗口,卖:96
一号窗口,卖:95
一号窗口,卖:94
二号窗口,卖:99
二号窗口,卖:92
一号窗口,卖:93
一号窗口,卖:88
一号窗口,卖:87
一号窗口,卖:86
二号窗口,卖:89
二号窗口,卖:84
二号窗口,卖:83
二号窗口,卖:82
二号窗口,卖:81
二号窗口,卖:80
二号窗口,卖:79
二号窗口,卖:78
二号窗口,卖:77
二号窗口,卖:76
二号窗口,卖:75
二号窗口,卖:74
二号窗口,卖:73
二号窗口,卖:72
二号窗口,卖:71
二号窗口,卖:70
二号窗口,卖:69
二号窗口,卖:68
二号窗口,卖:67
二号窗口,卖:66
二号窗口,卖:65
二号窗口,卖:64
二号窗口,卖:63
二号窗口,卖:62
二号窗口,卖:61
二号窗口,卖:60
二号窗口,卖:59
二号窗口,卖:58
二号窗口,卖:57
二号窗口,卖:56
二号窗口,卖:55
二号窗口,卖:54
二号窗口,卖:53
二号窗口,卖:52
二号窗口,卖:51
二号窗口,卖:50
二号窗口,卖:49
二号窗口,卖:48
二号窗口,卖:47
二号窗口,卖:46
二号窗口,卖:45
二号窗口,卖:44
二号窗口,卖:43
二号窗口,卖:42
二号窗口,卖:41
二号窗口,卖:40
二号窗口,卖:39
二号窗口,卖:38
二号窗口,卖:37
二号窗口,卖:36
二号窗口,卖:35
二号窗口,卖:34
二号窗口,卖:33
二号窗口,卖:32
二号窗口,卖:31
二号窗口,卖:30
二号窗口,卖:29
二号窗口,卖:28
二号窗口,卖:27
二号窗口,卖:26
二号窗口,卖:25
二号窗口,卖:24
二号窗口,卖:23
二号窗口,卖:22
二号窗口,卖:21
三号窗口,卖:90
四号窗口,卖:91
三号窗口,卖:19
二号窗口,卖:20
一号窗口,卖:85
二号窗口,卖:16
三号窗口,卖:17
四号窗口,卖:18
三号窗口,卖:13
三号窗口,卖:11
三号窗口,卖:10
三号窗口,卖:9
三号窗口,卖:8
三号窗口,卖:7
三号窗口,卖:6
三号窗口,卖:5
三号窗口,卖:4
三号窗口,卖:3
三号窗口,卖:2
三号窗口,卖:1
二号窗口,卖:14
一号窗口,卖:15
四号窗口,卖:12

两种创建线程方式:

  优点    缺点
继承Thread类

1.编写简单

2.可以使用this关键字直接访问当前线程

无法继承其它类

无法实现数据共享

实现Runnable接口

1.可以继承其它类

2.多个线程之间可以使用同一个Runnable对象

3.可以共享数据

编程方式稍微复杂,如需访问当前线程,需要调用Thread类的currentThread()方法                                                                                                              

                                   
                                 

六、线程安全

6.1、还是上面的例子

加上sleep(1000)睡觉1秒

package com.day10.thread.demo1;

public class Ticket implements Runnable {
private int num=100; @Override
public void run() { while(true){
if(num>0){
try {
Thread.sleep(1000); //当前线程睡觉1秒,毫秒单位
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖出"+num--+"号票");
}
}
}
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
} }

结果:

Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Thread-0,卖出100号票
Thread-2,卖出99号票
Thread-1,卖出98号票
Thread-3,卖出97号票
Thread-0,卖出96号票
Thread-2,卖出95号票
Thread-1,卖出94号票
Thread-3,卖出93号票
Thread-0,卖出92号票
Thread-2,卖出91号票
Thread-1,卖出90号票
Thread-3,卖出89号票
Thread-0,卖出88号票
Thread-2,卖出87号票
Thread-1,卖出86号票
Thread-3,卖出85号票
Thread-0,卖出84号票
Thread-2,卖出83号票
Thread-1,卖出82号票
Thread-3,卖出81号票
Thread-0,卖出80号票
Thread-2,卖出79号票
Thread-1,卖出78号票
Thread-3,卖出77号票
Thread-0,卖出76号票
Thread-2,卖出75号票
Thread-1,卖出74号票
Thread-3,卖出73号票
Thread-0,卖出72号票
Thread-2,卖出71号票
Thread-1,卖出70号票
Thread-3,卖出69号票
Thread-0,卖出68号票
Thread-2,卖出67号票
Thread-1,卖出66号票
Thread-3,卖出65号票
Thread-0,卖出64号票
Thread-2,卖出63号票
Thread-1,卖出62号票
Thread-3,卖出61号票
Thread-0,卖出60号票
Thread-2,卖出59号票
Thread-1,卖出58号票
Thread-3,卖出57号票
Thread-0,卖出56号票
Thread-2,卖出55号票
Thread-1,卖出54号票
Thread-3,卖出53号票
Thread-0,卖出52号票
Thread-2,卖出51号票
Thread-1,卖出50号票
Thread-3,卖出49号票
Thread-0,卖出48号票
Thread-2,卖出47号票
Thread-1,卖出46号票
Thread-3,卖出45号票
Thread-0,卖出44号票
Thread-2,卖出43号票
Thread-1,卖出42号票
Thread-3,卖出41号票
Thread-0,卖出40号票
Thread-2,卖出39号票
Thread-1,卖出38号票
Thread-3,卖出37号票
Thread-0,卖出36号票
Thread-2,卖出35号票
Thread-1,卖出34号票
Thread-3,卖出33号票
Thread-0,卖出32号票
Thread-2,卖出31号票
Thread-1,卖出30号票
Thread-3,卖出29号票
Thread-0,卖出28号票
Thread-2,卖出27号票
Thread-1,卖出26号票
Thread-3,卖出25号票
Thread-0,卖出24号票
Thread-2,卖出23号票
Thread-1,卖出22号票
Thread-3,卖出21号票
Thread-0,卖出20号票
Thread-2,卖出19号票
Thread-1,卖出18号票
Thread-3,卖出17号票
Thread-0,卖出16号票
Thread-2,卖出15号票
Thread-1,卖出14号票
Thread-3,卖出13号票
Thread-0,卖出12号票
Thread-2,卖出11号票
Thread-1,卖出10号票
Thread-3,卖出9号票
Thread-0,卖出8号票
Thread-2,卖出7号票
Thread-1,卖出6号票
Thread-3,卖出5号票
Thread-0,卖出4号票
Thread-2,卖出3号票
Thread-1,卖出2号票
Thread-3,卖出1号票
Thread-0,卖出0号票
Thread-2,卖出-1号票
Thread-1,卖出-2号票

发现多卖票了,

为什么?因为卖票当前线程睡眠了1秒,其它线程就可以把这张票已经卖出了,

条件检查1>0,如果4个人同时检查,条件成立,就继续卖票就出现了,负数

怎么解决?给线程加锁

线程安全问题产生的原因:

1. 多个线程在操作共享的数据。
    2. 操作共享数据的线程代码有多条。
    当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

解决方式:就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

看下面的同步synchronized

七、同步代码块

7.1、synchronized

同步代码块的格式:
    synchronized(对象){
           需要被同步的代码;
    } 
    同步的好处:解决了线程的安全问题。
    同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
    同步的前提:必须有多个线程并使用同一个锁。

修改上面的代码

package com.day10.thread.demo1;

public class Ticket implements Runnable {
private int num=100; @Override
public void run() { while(true){
synchronized(this){
if(num>0){
try {
Thread.sleep(1000); //当前线程睡觉1秒,毫秒单位
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖出"+num--+"号票");
}else{
break;
}
}
}
}
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
} }

八、同步函数 

8.1、同步方法

package com.day10.thread.demo2;

public class Account {

    //余额
private double blance=500; //取款
public void withDraw(int num){ blance=blance-num; } public double getBlance() {
return blance;
} public void setBlance(double blance) {
this.blance = blance;
} }
package com.day10.thread.demo2; public class AccountThread implements Runnable { private Account account=new Account();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if(account.getBlance()<0){
System.out.println("透支了!!");
}
makWithDraw(100); //每次取100
} } /*
* 取款的方法
*/
public void makWithDraw(int num){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
} }
package com.day10.thread.demo2; public class Demo { public static void main(String[] args) { AccountThread at=new AccountThread();
Thread t1=new Thread(at);
t1.setName("张三");
Thread t2=new Thread(at);
t2.setName("张三老婆");
t1.start();
t2.start();
} }

结果:

张三:准备取款!
张三老婆:准备取款!
张三:取款完成!,当前余额为:400.0
张三:准备取款!
张三老婆:取款完成!,当前余额为:300.0
张三老婆:准备取款!
张三:取款完成!,当前余额为:200.0
张三:准备取款!
张三老婆:取款完成!,当前余额为:100.0
张三老婆:准备取款!
张三:取款完成!,当前余额为:0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三老婆:取款完成!,当前余额为:-100.0
透支了!!
张三老婆 余额不足以支付当前取款, 余额为: -100.0
透支了!!
张三老婆 余额不足以支付当前取款, 余额为: -100.0

加上同步关键字

/*
* 取款的方法
*/
public synchronized void makWithDraw(int num){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
}

结果:

张三:准备取款!
张三:取款完成!,当前余额为:400.0
张三老婆:准备取款!
张三老婆:取款完成!,当前余额为:300.0
张三:准备取款!
张三:取款完成!,当前余额为:200.0
张三:准备取款!
张三:取款完成!,当前余额为:100.0
张三:准备取款!
张三:取款完成!,当前余额为:0.0
张三 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0
张三老婆 余额不足以支付当前取款, 余额为: 0.0

没有发生透支现象

同步代码块:

/*
* 取款的方法
*/
public void makWithDraw(int num){
synchronized(account){
//判断余额是不是大于要取款的数
if(account.getBlance()>=num){
System.out.println(Thread.currentThread().getName()+":准备取款!");
try {
Thread.sleep(1000); //等待1秒
account.withDraw(num);
System.out.println(Thread.currentThread().getName()+":取款完成!,当前余额为:"+account.getBlance());
} catch (InterruptedException e) {
e.printStackTrace();
} }else{
System.out.println(Thread.currentThread().getName()+" 余额不足以支付当前取款, 余额为: "+account.getBlance());
}
}
}

结果同上,没有发生透支现象

同步函数和同步代码块的区别:
    1. 同步函数的锁是固定的this。
    2. 同步代码块的锁是任意的对象。
    建议使用同步代码块。
    由于同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this作为锁,就可以实现同步。

九、同步锁

9.1、同锁

同步方法同步对象是this,同步代码块也可以使用this来同步

十、静态同步

10.1、静态同步

不是this,因为静态方法中也不可以定义this

静态进内存,内存中没有本类对象,但是一定有该类对应的字节码文件对象

静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示。

synchronizid(类名.class){  或者对象.getClass

}

十一、单例模式加上同步懒汉式

11.1、懒汉式加锁

package com.day10.thread.demo2;
/**
*
* @author denny
*单例模式
*饿汉式,不存在复生成对象的问题
*懒汉工,加synchronize
*/
public class SingleDemo {
private static SingleDemo singleDemo; private SingleDemo(){ }
public static SingleDemo getNewIntance(){
if(singleDemo==null){//判断是不是空,不是空就直接返回,是空就加锁
synchronized(SingleDemo.class){ //加锁 //this.getClass() 直接写对象singleDemo.getClass()也行 if(singleDemo==null){
singleDemo=new SingleDemo();
}
}
}
return singleDemo; } }

十二、死锁

12.1、死锁常见情景之一:同步的嵌套。

 class MyLock{
static Object lockA=new Object();
static Object lockB=new Object();
} public class Demo implements Runnable { private boolean flag; public Demo(boolean flag) {
this.flag = flag;
} @Override
public void run() { if (flag) {
while (true) {
synchronized (MyLock.lockA) {
System.out.println("if.........lockA");
synchronized (MyLock.lockB) {
System.out.println("if.........lockB");
}
}
}
}
else {
while(true){
synchronized (MyLock.lockB) {
System.out.println("else.........lockB");
synchronized (MyLock.lockA) {
System.out.println("else.........lockA");
}
}
} }
} public static void main(String[] args) {
Thread t1 = new Thread(new Demo(true));
Thread t2 = new Thread(new Demo(false));
t1.start();
t2.start();
} }

建议将要锁的对象,声明为static

基础学习day11--多线程一线程的创建,运行,同步和锁的更多相关文章

  1. Java多线程之线程的创建

    好久没有更博客了,最近一直在忙工作的事情.现在终于空下来了,这2天会抓紧时间整理多线程和socket,把JavaSE结束掉. 关于多线程,首先会涉及到哪些东西呢?首先要了解线程,为什么要使用线程,线程 ...

  2. C/C++ Muti-Thread多线程编程学习(之)线程Thread | 创建、运行、结束

    文章目录 前言 线程 Thread 创建线程 CreateThread _beginthread _beginthreadex pthread_create 线程运行 结束线程 前言   多线程(Mu ...

  3. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  4. Linux多线程编程——线程的创建与退出

    POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...

  5. .net学习之多线程、线程死锁、线程通信 生产者消费者模式、委托的简单使用、GDI(图形设计接口)常用的方法

    1.多线程简单使用(1)进程是不执行代码的,执行代码的是线程,一个进程默认有一个线程(2)线程默认情况下都是前台线程,要所有的前台线程退出以后程序才会退出,进程里默认的线程我们叫做主线程或者叫做UI线 ...

  6. 黑马程序员——JAVA基础之简述多线程,两种创建多线程的方式

    ------- android培训.java培训.期待与您交流! ---------- 多线程: 进程和线程: 进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者 ...

  7. Java并发学习之中的一个——线程的创建

    本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...

  8. java学习笔记 --- 多线程(线程安全问题——同步代码块)

    1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...

  9. (C#- 多线程) 在线程中创建object,共享问题。

    研究如下问题: 1. 在一个进程的主线程中创建一个Object,其他线程都可以访问这个Object,并操作Object的方法. - 多线程同步问题. 2. 在一个进程的多个线程里面,每个线程都创建同一 ...

  10. 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题

    调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...

随机推荐

  1. smartjs 0.2 OOP讲解 - Klass 类继承

    SmartJS2.0加入OOP的功能.OOP包括klass与factory两个对象. Klass 类继承 与其他的类继承相比,smartjs使用了执行指针的概念(后面例子中会介绍),另外提供base基 ...

  2. Asphyre 更名pxl 终于全面支持跨平台了.Delphi饭们 激动了吧.

    Asphyre We are happy to announce the official release of our latest framework Pascal eXtended Librar ...

  3. CentOS6部署VNC服务端

    VNC (Virtual Network Computer)是虚拟网络计算机的缩写.VNC 是在基于 UNIX 和 Linux 操作系统的免费的开源软件,远程控制能力强大,高效实用,其性能可以和 Wi ...

  4. c# File 操作

    //1.---------文件夹创建.移动.删除--------- //创建文件夹 Directory.CreateDirectory(Server.MapPath("a")); ...

  5. ruby -- 问题解决(一)无法连接mysql数据库

     >rails g controller home index  运行该命令时无法连接mysql 先下载配置文件:mysql-connector-c-noinstall-6.0.2-win32. ...

  6. MongoDB 基础命令行

    本文专门介绍MongoDB的命令行操作.其实,这些操作在MongoDB官网提供的Quick Reference上都有,但是英文的,为了方便,这里将其稍微整理下,方便查阅. 登录和退出 mongo命令直 ...

  7. 使用C语言描述静态链表和动态链表

    静态链表和动态链表是线性表链式存储结构的两种不同的表示方式. 静态链表的初始长度一般是固定的,在做插入和删除操作时不需要移动元素,仅需修改指针,故仍具有链式存储结构的主要优点. 动态链表是相对于静态链 ...

  8. 【HTML5】Canvas 实现放大镜效果

    图片放大镜 效果 在线演示    源码 原理 首先选择图片的一块区域,然后将这块区域放大,然后再绘制到原先的图片上,保证两块区域的中心点一致, 如下图所示: 初始化 <canvas id=&qu ...

  9. 本学期3个sprint的团队贡献分

    第一次冲刺贡献分 组员 贡献分 103马嘉诚 28 143李新佳 22 145马文其 19 120韩智豪 16 147黄鸿浩 15 第二次冲刺贡献分 组员 贡献分 103马嘉诚 23 143李新佳 2 ...

  10. TortoiseSVN的bin目录下面没有svn.exe

    自己在idea联合svn时遇到这个问题,然后bd和gg,发现很多人都乱说,说什么TortoiseSVN是客户端,默认不包含svn.exe,需要安装Subversion.bullshit! 之所以没有, ...